diff options
Diffstat (limited to 'src/chunkmeta.rs')
-rw-r--r-- | src/chunkmeta.rs | 114 |
1 files changed, 51 insertions, 63 deletions
diff --git a/src/chunkmeta.rs b/src/chunkmeta.rs index 37e2ed5..e2fa9b3 100644 --- a/src/chunkmeta.rs +++ b/src/chunkmeta.rs @@ -1,28 +1,21 @@ +//! Metadata about a chunk. + +use crate::label::Label; use serde::{Deserialize, Serialize}; use std::default::Default; use std::str::FromStr; /// Metadata about chunks. /// -/// We manage three bits of metadata about chunks, in addition to its -/// identifier: -/// -/// * for all chunks, a [SHA256][] checksum of the chunk content -/// -/// * for generation chunks, an indication that it is a generation -/// chunk, and a timestamp for when making the generation snapshot -/// ended -/// -/// There is no syntax or semantics imposed on the timestamp, but a -/// client should probably use [ISO 8601][] representation. +/// We a single piece of metadata about chunks, in addition to its +/// identifier: a label assigned by the client. Currently, this is a +/// [SHA256][] checksum of the chunk content. /// /// For HTTP, the metadata will be serialised as a JSON object, like this: /// /// ~~~json /// { -/// "sha256": "09ca7e4eaa6e8ae9c7d261167129184883644d07dfba7cbfbc4c8a2e08360d5b", -/// "generation": true, -/// "ended": "2020-09-17T08:17:13+03:00" +/// "label": "09ca7e4eaa6e8ae9c7d261167129184883644d07dfba7cbfbc4c8a2e08360d5b", /// } /// ~~~ /// @@ -35,55 +28,44 @@ use std::str::FromStr; /// /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601 /// [SHA256]: https://en.wikipedia.org/wiki/SHA-2 -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] pub struct ChunkMeta { - sha256: String, - // The remaining fields are Options so that JSON parsing doesn't - // insist on them being there in the textual representation. - generation: Option<bool>, - ended: Option<String>, + label: String, } impl ChunkMeta { /// Create a new data chunk. /// /// Data chunks are not for generations. - pub fn new(sha256: &str) -> Self { + pub fn new(label: &Label) -> Self { ChunkMeta { - sha256: sha256.to_string(), - generation: None, - ended: None, + label: label.serialize(), } } - /// Create a new generation chunk. - pub fn new_generation(sha256: &str, ended: &str) -> Self { - ChunkMeta { - sha256: sha256.to_string(), - generation: Some(true), - ended: Some(ended.to_string()), - } - } - - /// Is this a generation chunk? - pub fn is_generation(&self) -> bool { - matches!(self.generation, Some(true)) - } - - /// When did this generation end? - pub fn ended(&self) -> Option<&str> { - self.ended.as_deref() + /// The label of the content of the chunk. + /// + /// The caller should not interpret the label in any way. It + /// happens to be a SHA256 of the cleartext contents of the + /// checksum for now, but that _will_ change in the future. + pub fn label(&self) -> &str { + &self.label } - /// SHA256 checksum of the content of the chunk. - pub fn sha256(&self) -> &str { - &self.sha256 + /// Serialize from a textual JSON representation. + pub fn from_json(json: &str) -> Result<Self, serde_json::Error> { + serde_json::from_str(json) } /// Serialize as JSON. pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap() } + + /// Serialize as JSON, as a byte vector. + pub fn to_json_vec(&self) -> Vec<u8> { + self.to_json().as_bytes().to_vec() + } } impl FromStr for ChunkMeta { @@ -97,48 +79,54 @@ impl FromStr for ChunkMeta { #[cfg(test)] mod test { - use super::ChunkMeta; + use super::{ChunkMeta, Label}; #[test] fn new_creates_data_chunk() { - let meta = ChunkMeta::new("abcdef"); - assert!(!meta.is_generation()); - assert_eq!(meta.ended(), None); - assert_eq!(meta.sha256(), "abcdef"); + let sum = Label::sha256(b"abcdef"); + let meta = ChunkMeta::new(&sum); + assert_eq!(meta.label(), sum.serialize()); } #[test] fn new_generation_creates_generation_chunk() { - let meta = ChunkMeta::new_generation("abcdef", "2020-09-17T08:17:13+03:00"); - assert!(meta.is_generation()); - assert_eq!(meta.ended(), Some("2020-09-17T08:17:13+03:00")); - assert_eq!(meta.sha256(), "abcdef"); + let sum = Label::sha256(b"abcdef"); + let meta = ChunkMeta::new(&sum); + assert_eq!(meta.label(), sum.serialize()); } #[test] fn data_chunk_from_json() { - let meta: ChunkMeta = r#"{"sha256": "abcdef"}"#.parse().unwrap(); - assert!(!meta.is_generation()); - assert_eq!(meta.ended(), None); - assert_eq!(meta.sha256(), "abcdef"); + let meta: ChunkMeta = r#"{"label": "abcdef"}"#.parse().unwrap(); + assert_eq!(meta.label(), "abcdef"); } #[test] fn generation_chunk_from_json() { let meta: ChunkMeta = - r#"{"sha256": "abcdef", "generation": true, "ended": "2020-09-17T08:17:13+03:00"}"# + r#"{"label": "abcdef", "generation": true, "ended": "2020-09-17T08:17:13+03:00"}"# .parse() .unwrap(); - assert!(meta.is_generation()); - assert_eq!(meta.ended(), Some("2020-09-17T08:17:13+03:00")); - assert_eq!(meta.sha256(), "abcdef"); + + assert_eq!(meta.label(), "abcdef"); } #[test] - fn json_roundtrip() { - let meta = ChunkMeta::new_generation("abcdef", "2020-09-17T08:17:13+03:00"); + fn generation_json_roundtrip() { + let sum = Label::sha256(b"abcdef"); + let meta = ChunkMeta::new(&sum); let json = serde_json::to_string(&meta).unwrap(); let meta2 = serde_json::from_str(&json).unwrap(); assert_eq!(meta, meta2); } + + #[test] + fn data_json_roundtrip() { + let sum = Label::sha256(b"abcdef"); + let meta = ChunkMeta::new(&sum); + let json = meta.to_json_vec(); + let meta2 = serde_json::from_slice(&json).unwrap(); + assert_eq!(meta, meta2); + assert_eq!(meta.to_json_vec(), meta2.to_json_vec()); + } } |