From 815d7a3c9e880eccd89baf4565e627658c5ac553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Wed, 5 Jun 2019 11:34:30 +0200 Subject: [PATCH] feat(put): initial implementation of cacache::put --- src/index.rs | 105 ++++++++++++++++----------------------------------- src/lib.rs | 1 + src/put.rs | 82 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 73 deletions(-) create mode 100644 src/put.rs diff --git a/src/index.rs b/src/index.rs index a4f773a..ad2ef3a 100644 --- a/src/index.rs +++ b/src/index.rs @@ -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, time: u128, - size: u128, + size: usize, metadata: Value, } -pub struct Inserter { - cache: PathBuf, - key: String, - sri: Option, - size: Option, - time: Option, - metadata: Option, - uid: Option, - gid: Option, -} - -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, gid: Option) -> Self { - self.uid = uid; - self.gid = gid; - self - } - - pub fn commit(self) -> Result { - 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::().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 { + 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::().unwrap())) } pub fn find(cache: &Path, key: &str) -> Result, Error> { @@ -131,7 +84,7 @@ pub fn find(cache: &Path, key: &str) -> Result, 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); } diff --git a/src/lib.rs b/src/lib.rs index 2b13582..79e5ffe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ mod content; pub mod get; +pub mod put; mod index; mod errors; diff --git a/src/put.rs b/src/put.rs new file mode 100644 index 0000000..9c3fbca --- /dev/null +++ b/src/put.rs @@ -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) -> Result { + 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, + pub size: Option, + pub time: Option, + pub metadata: Option, + pub uid: Option, + pub gid: Option, +} + +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, gid: Option) -> Self { + self.uid = uid; + self.gid = gid; + self + } + + pub fn commit(self, data: Vec) -> Result { + 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) + } +}