Finalize deserializing ncdu output

This commit is contained in:
Austen Adler 2023-11-15 22:13:12 -05:00
parent 84a4f09fe4
commit 80bb5bbb22
3 changed files with 60 additions and 47 deletions

11
Cargo.lock generated
View File

@ -28,6 +28,7 @@ dependencies = [
"num-traits", "num-traits",
"serde", "serde",
"serde_json", "serde_json",
"serde_path_to_error",
"serde_repr", "serde_repr",
] ]
@ -95,6 +96,16 @@ dependencies = [
"serde", "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]] [[package]]
name = "serde_repr" name = "serde_repr"
version = "0.1.17" version = "0.1.17"

View File

@ -10,3 +10,6 @@ num-traits = "0.2.17"
serde = { version = "1.0.192", features = ["derive"] } serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108" serde_json = "1.0.108"
serde_repr = "0.1.17" serde_repr = "0.1.17"
[dev-dependencies]
serde_path_to_error = "0.1.14"

View File

@ -3,7 +3,7 @@
use num_traits::identities::One; use num_traits::identities::One;
use num_traits::identities::Zero; use num_traits::identities::Zero;
use serde::de; use serde::de;
use serde::de::Error; // use serde::de::Error;
use serde::de::Visitor; use serde::de::Visitor;
use serde_repr::Deserialize_repr; use serde_repr::Deserialize_repr;
use serde_repr::Serialize_repr; use serde_repr::Serialize_repr;
@ -16,30 +16,26 @@ use serde::{Deserialize, Serialize};
// This is based on https://dev.yorhel.nl/ncdu/jsonfmt // This is based on https://dev.yorhel.nl/ncdu/jsonfmt
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[derive(Serialize, Debug, PartialEq, Clone)]
struct NcduFile { struct NcduFile {
#[serde(flatten)]
header: Header,
}
#[derive(Serialize, Debug, PartialEq, Eq, Clone)]
pub struct Header {
pub major_version: MajorVersion, pub major_version: MajorVersion,
pub minor_version: MinorVersion, pub minor_version: MinorVersion,
pub header_metadata: HeaderMetadata, pub header_metadata: HeaderMetadata,
#[serde(flatten)]
pub contents: Directory,
} }
impl<'de> Deserialize<'de> for Header { impl<'de> Deserialize<'de> for NcduFile {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
struct HeaderVisitor; struct NcduFileVisitor;
impl<'de> Visitor<'de> for HeaderVisitor { impl<'de> Visitor<'de> for NcduFileVisitor {
type Value = Header; type Value = NcduFile;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct Header") formatter.write_str("struct NcduFile")
} }
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error> fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
@ -56,25 +52,21 @@ impl<'de> Deserialize<'de> for Header {
.next_element()? .next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?; .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, major_version,
minor_version, minor_version,
header_metadata, header_metadata,
contents,
}) })
} }
} }
deserializer.deserialize_seq(HeaderVisitor) deserializer.deserialize_seq(NcduFileVisitor)
}
}
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),
}
} }
} }
@ -117,7 +109,8 @@ pub struct HeaderMetadata {
} }
impl 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 { Self {
progname, progname,
progver, progver,
@ -142,8 +135,8 @@ impl<'de> Deserialize<'de> for Directory {
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
struct HeaderVisitor; struct DirectoryVisitor;
impl<'de> Visitor<'de> for HeaderVisitor { impl<'de> Visitor<'de> for DirectoryVisitor {
type Value = Directory; type Value = Directory;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
@ -157,6 +150,8 @@ impl<'de> Deserialize<'de> for Directory {
let info = seq let info = seq
.next_element()? .next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?; .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() { let mut contents = if let Some(size_hint) = seq.size_hint() {
Vec::with_capacity(size_hint) Vec::with_capacity(size_hint)
} else { } 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)] #[cfg(test)]
mod tests { mod tests {
use std::fs;
use super::*; 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>(header_text));
assert!(parsed.is_ok());
assert!(parsed.unwrap().minor_version == MinorVersion::Minor2);
}
#[test] #[test]
fn test_header_metadata() { fn test_header_metadata() {
let header_text = r#"{ let header_text = r#"{
@ -374,4 +353,24 @@ mod tests {
)) ))
.is_ok()); .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::<NcduFile>(&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);
}
} }