feat(put): initial implementation of cacache::put

This commit is contained in:
Kat Marchán 2019-06-05 11:34:30 +02:00
parent 0bbe080a6e
commit 815d7a3c9e
No known key found for this signature in database
GPG Key ID: AEB529C08A3C7E9E
3 changed files with 115 additions and 73 deletions

View File

@ -7,13 +7,13 @@ use chownr;
use digest::Digest;
use hex;
use mkdirp;
use nix::unistd::{Uid, Gid};
use serde_derive::{Deserialize, Serialize};
use serde_json::{json, Value};
use sha1::Sha1;
use sha2::Sha256;
use ssri::Integrity;
use crate::put::Writer;
use crate::errors::Error;
const INDEX_VERSION: &str = "5";
@ -23,7 +23,7 @@ pub struct Entry {
pub key: String,
pub integrity: Integrity,
pub time: u128,
pub size: u128,
pub size: usize,
pub metadata: Value,
}
@ -32,77 +32,30 @@ struct SerializableEntry {
key: String,
integrity: Option<String>,
time: u128,
size: u128,
size: usize,
metadata: Value,
}
pub struct Inserter {
cache: PathBuf,
key: String,
sri: Option<Integrity>,
size: Option<u128>,
time: Option<u128>,
metadata: Option<Value>,
uid: Option<Uid>,
gid: Option<Gid>,
}
impl Inserter {
pub fn size(mut self, size: u128) -> Self {
self.size = Some(size);
self
}
pub fn metadata(mut self, metadata: Value) -> Self {
self.metadata = Some(metadata);
self
}
pub fn time(mut self, time: u128) -> Self {
self.time = Some(time);
self
}
pub fn chown(mut self, uid: Option<Uid>, gid: Option<Gid>) -> Self {
self.uid = uid;
self.gid = gid;
self
}
pub fn commit(self) -> Result<Integrity, Error> {
let bucket = bucket_path(&self.cache, &self.key);
if let Some(path) = mkdirp::mkdirp(bucket.parent().unwrap())? {
chownr::chownr(&path, self.uid, self.gid)?;
}
let stringified = serde_json::to_string(&SerializableEntry {
key: self.key.to_owned(),
integrity: self.sri.clone().map(|x| x.to_string()),
time: self.time.unwrap_or_else(now),
size: self.size.unwrap_or(0),
metadata: self.metadata.unwrap_or_else(|| json!(null)),
})?;
let str = format!("\n{}\t{}", hash_entry(&stringified), stringified);
OpenOptions::new()
.create(true)
.append(true)
.open(&bucket)?
.write_all(&str.into_bytes())?;
chownr::chownr(&bucket, self.uid, self.gid)?;
Ok(self.sri.unwrap_or_else(|| "sha1-deadbeef".parse::<Integrity>().unwrap()))
}
}
pub fn insert(cache: &Path, key: &str, sri: Integrity) -> Inserter {
Inserter {
cache: cache.to_path_buf(),
key: String::from(key),
size: None,
sri: Some(sri),
time: None,
metadata: None,
uid: None,
gid: None,
pub fn insert(inserter: Writer) -> Result<Integrity, Error> {
let bucket = bucket_path(&inserter.cache, &inserter.key);
if let Some(path) = mkdirp::mkdirp(bucket.parent().unwrap())? {
chownr::chownr(&path, inserter.uid, inserter.gid)?;
}
let stringified = serde_json::to_string(&SerializableEntry {
key: inserter.key.to_owned(),
integrity: inserter.sri.clone().map(|x| x.to_string()),
time: inserter.time.unwrap_or_else(now),
size: inserter.size.unwrap_or(0),
metadata: inserter.metadata.unwrap_or_else(|| json!(null)),
})?;
let str = format!("\n{}\t{}", hash_entry(&stringified), stringified);
OpenOptions::new()
.create(true)
.append(true)
.open(&bucket)?
.write_all(&str.into_bytes())?;
chownr::chownr(&bucket, inserter.uid, inserter.gid)?;
Ok(inserter.sri.unwrap_or_else(|| "sha1-deadbeef".parse::<Integrity>().unwrap()))
}
pub fn find(cache: &Path, key: &str) -> Result<Option<Entry>, Error> {
@ -131,7 +84,7 @@ pub fn find(cache: &Path, key: &str) -> Result<Option<Entry>, Error> {
}
pub fn delete(cache: &Path, key: &str) -> Result<(), Error> {
let inserter = Inserter {
let inserter = Writer {
cache: cache.to_path_buf(),
key: String::from(key),
size: None,
@ -141,7 +94,7 @@ pub fn delete(cache: &Path, key: &str) -> Result<(), Error> {
uid: None,
gid: None,
};
inserter.commit()?;
insert(inserter)?;
Ok(())
}
@ -220,7 +173,10 @@ mod tests {
let dir = tmp.path().to_owned();
let sri: Integrity = "sha1-deadbeef".parse().unwrap();
let time = 1_234_567;
insert(&dir, "hello", sri).time(time).commit().unwrap();
let writer = Writer::new(&dir, "hello")
.integrity(sri)
.time(time);
insert(writer).unwrap();
let entry = std::fs::read_to_string(bucket_path(&dir, "hello")).unwrap();
assert_eq!(entry, MOCK_ENTRY);
}
@ -260,7 +216,10 @@ mod tests {
let dir = tmp.path().to_owned();
let sri: Integrity = "sha1-deadbeef".parse().unwrap();
let time = 1_234_567;
insert(&dir, "hello", sri).time(time).commit().unwrap();
let writer = Writer::new(&dir, "hello")
.integrity(sri)
.time(time);
insert(writer).unwrap();
delete(&dir, "hello").unwrap();
assert_eq!(find(&dir, "hello").unwrap(), None);
}

View File

@ -1,5 +1,6 @@
mod content;
pub mod get;
pub mod put;
mod index;
mod errors;

82
src/put.rs Normal file
View File

@ -0,0 +1,82 @@
use std::path::{Path, PathBuf};
use nix::unistd::{Uid, Gid};
use serde_json::Value;
use ssri::Integrity;
use crate::content::write;
use crate::index;
use crate::errors::Error;
pub fn data(cache: &Path, key: String, data: Vec<u8>) -> Result<Integrity, Error> {
let sri = write::write(&cache, &data)?;
Writer::new(cache, &key).integrity(sri).commit(data)
}
pub struct Writer {
pub cache: PathBuf,
pub key: String,
pub sri: Option<Integrity>,
pub size: Option<usize>,
pub time: Option<u128>,
pub metadata: Option<Value>,
pub uid: Option<Uid>,
pub gid: Option<Gid>,
}
impl Writer {
pub fn new(cache: &Path, key: &str) -> Writer {
Writer {
cache: cache.to_path_buf(),
key: String::from(key),
sri: None,
size: None,
time: None,
metadata: None,
uid: None,
gid: None
}
}
pub fn size(mut self, size: usize) -> Self {
self.size = Some(size);
self
}
pub fn metadata(mut self, metadata: Value) -> Self {
self.metadata = Some(metadata);
self
}
pub fn time(mut self, time: u128) -> Self {
self.time = Some(time);
self
}
pub fn integrity(mut self, sri: Integrity) -> Self {
self.sri = Some(sri);
self
}
pub fn chown(mut self, uid: Option<Uid>, gid: Option<Gid>) -> Self {
self.uid = uid;
self.gid = gid;
self
}
pub fn commit(self, data: Vec<u8>) -> Result<Integrity, Error> {
if let Some(sri) = &self.sri {
if sri.clone().check(&data).is_none() {
return Err(Error::IntegrityError);
}
}
if let Some(size) = self.size {
if size != data.len() {
return Err(Error::SizeError);
}
}
let sri = write::write(&self.cache, &data)?;
index::insert(self)?;
Ok(sri)
}
}