diff --git a/Cargo.lock b/Cargo.lock index 165e76a..cd7b7c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,7 @@ dependencies = [ "num-traits", "serde", "serde_json", + "serde_path_to_error", "serde_repr", ] @@ -95,6 +96,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.17" diff --git a/ncdufmt/Cargo.toml b/ncdufmt/Cargo.toml index f32b95f..12482b8 100644 --- a/ncdufmt/Cargo.toml +++ b/ncdufmt/Cargo.toml @@ -10,3 +10,6 @@ num-traits = "0.2.17" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" serde_repr = "0.1.17" + +[dev-dependencies] +serde_path_to_error = "0.1.14" diff --git a/ncdufmt/src/lib.rs b/ncdufmt/src/lib.rs index 397a430..76cc0c4 100644 --- a/ncdufmt/src/lib.rs +++ b/ncdufmt/src/lib.rs @@ -3,7 +3,7 @@ use num_traits::identities::One; use num_traits::identities::Zero; use serde::de; -use serde::de::Error; +// use serde::de::Error; use serde::de::Visitor; use serde_repr::Deserialize_repr; use serde_repr::Serialize_repr; @@ -16,30 +16,26 @@ use serde::{Deserialize, Serialize}; // This is based on https://dev.yorhel.nl/ncdu/jsonfmt -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Debug, PartialEq, Clone)] struct NcduFile { - #[serde(flatten)] - header: Header, -} - -#[derive(Serialize, Debug, PartialEq, Eq, Clone)] -pub struct Header { pub major_version: MajorVersion, pub minor_version: MinorVersion, pub header_metadata: HeaderMetadata, + #[serde(flatten)] + pub contents: Directory, } -impl<'de> Deserialize<'de> for Header { +impl<'de> Deserialize<'de> for NcduFile { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - struct HeaderVisitor; - impl<'de> Visitor<'de> for HeaderVisitor { - type Value = Header; + struct NcduFileVisitor; + impl<'de> Visitor<'de> for NcduFileVisitor { + type Value = NcduFile; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct Header") + formatter.write_str("struct NcduFile") } fn visit_seq(self, mut seq: V) -> Result @@ -56,25 +52,21 @@ impl<'de> Deserialize<'de> for Header { .next_element()? .ok_or_else(|| de::Error::invalid_length(2, &self))?; - Ok(Header { + let contents = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + + Ok(NcduFile { major_version, minor_version, header_metadata, + + contents, }) } } - deserializer.deserialize_seq(HeaderVisitor) - } -} - -impl Header { - pub fn from(progname: String, progver: String) -> Self { - Self { - major_version: MajorVersion::default(), - minor_version: MinorVersion::default(), - header_metadata: HeaderMetadata::new(progname, progver), - } + deserializer.deserialize_seq(NcduFileVisitor) } } @@ -117,7 +109,8 @@ pub struct HeaderMetadata { } impl HeaderMetadata { - fn new(progname: String, progver: String) -> Self { + /// Construct a HeaderMetadata with the current time + pub fn new(progname: String, progver: String) -> Self { Self { progname, progver, @@ -142,8 +135,8 @@ impl<'de> Deserialize<'de> for Directory { where D: serde::Deserializer<'de>, { - struct HeaderVisitor; - impl<'de> Visitor<'de> for HeaderVisitor { + struct DirectoryVisitor; + impl<'de> Visitor<'de> for DirectoryVisitor { type Value = Directory; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -157,6 +150,8 @@ impl<'de> Deserialize<'de> for Directory { let info = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(0, &self))?; + + // TODO: Defer to vec deserialization let mut contents = if let Some(size_hint) = seq.size_hint() { Vec::with_capacity(size_hint) } else { @@ -171,7 +166,7 @@ impl<'de> Deserialize<'de> for Directory { } } - deserializer.deserialize_seq(HeaderVisitor) + deserializer.deserialize_seq(DirectoryVisitor) } } @@ -277,26 +272,10 @@ pub struct ExtendedInfoBlock { #[cfg(test)] mod tests { + use std::fs; + use super::*; - #[test] - fn test_header() { - let header_text = r#"[ - 1, - 2, - { - "progname": "Test program name", - "progver": "Version One", - "timestamp": 0 - } - ]"#; - - let parsed = dbg!(serde_json::from_str::
(header_text)); - - assert!(parsed.is_ok()); - assert!(parsed.unwrap().minor_version == MinorVersion::Minor2); - } - #[test] fn test_header_metadata() { let header_text = r#"{ @@ -374,4 +353,24 @@ mod tests { )) .is_ok()); } + + use serde_path_to_error; + #[test] + fn test_simple_ncdu_output() { + let input = fs::read_to_string("../sample-ncdu-output.json").unwrap(); + let jd = &mut serde_json::Deserializer::from_str(&input); + let parsed = serde_path_to_error::deserialize::<_, NcduFile>(jd) + .map_err(|e| { + eprintln!("Error path: {:?}", e.path().to_string()); + e + }) + .unwrap(); + // let parsed = serde_path_to_error::from_str::(&input).unwrap(); + + assert_eq!(parsed.major_version, MajorVersion::Version1); + assert_eq!(parsed.minor_version, MinorVersion::Minor2); + assert_eq!(parsed.header_metadata.progname, "ncdu"); + assert_eq!(parsed.contents.info.name, "/tmp/tmp.2gWrgcHU4X"); + assert_eq!(parsed.contents.contents.len(), 2); + } }