From 8b96cb77012c00acb67a24c84211f76728b43fbe Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sat, 5 Jun 2021 15:11:02 +0700 Subject: [PATCH 1/6] api+json: Reform `JsonData` into a struct --- src/app.rs | 13 +++++++------ src/app/sub.rs | 9 +++++---- src/json_data.rs | 15 ++++++++++++--- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/app.rs b/src/app.rs index 7224995d..c91431e2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,7 +4,7 @@ pub use sub::Sub; use crate::{ args::{Args, Quantity}, - json_data::JsonData, + json_data::{JsonData, UnitAndTree}, reporter::{ErrorOnlyReporter, ErrorReport, ProgressAndErrorReporter, ProgressReport}, runtime_error::RuntimeError, size::{Bytes, Size}, @@ -59,9 +59,10 @@ impl App { } = self.args; let direction = Direction::from_top_down(top_down); - let json_data = stdin() + let unit_and_tree = stdin() .pipe(serde_json::from_reader::<_, JsonData>) - .map_err(RuntimeError::DeserializationFailure)?; + .map_err(RuntimeError::DeserializationFailure)? + .unit_and_tree; macro_rules! visualize { ($reflection:expr, $bytes_format: expr) => {{ @@ -79,9 +80,9 @@ impl App { }}; } - let visualization = match json_data { - JsonData::Bytes(reflection) => visualize!(reflection, bytes_format), - JsonData::Blocks(reflection) => visualize!(reflection, ()), + let visualization = match unit_and_tree { + UnitAndTree::Bytes(reflection) => visualize!(reflection, bytes_format), + UnitAndTree::Blocks(reflection) => visualize!(reflection, ()), }; print!("{}", visualization); // it already ends with "\n", println! isn't needed here. diff --git a/src/app/sub.rs b/src/app/sub.rs index 017ae65d..bc61f06a 100644 --- a/src/app/sub.rs +++ b/src/app/sub.rs @@ -2,7 +2,7 @@ use crate::{ args::Fraction, data_tree::{DataTree, DataTreeReflection}, fs_tree_builder::FsTreeBuilder, - json_data::JsonData, + json_data::{JsonData, UnitAndTree}, os_string_display::OsStringDisplay, reporter::ParallelReporter, runtime_error::RuntimeError, @@ -19,7 +19,7 @@ where Data: Size + Into + Serialize + Send + Sync, Report: ParallelReporter + Sync, GetData: Fn(&Metadata) -> Data + Copy + Sync, - DataTreeReflection: Into, + DataTreeReflection: Into, { /// List of files and/or directories. pub files: Vec, @@ -48,7 +48,7 @@ where Data: Size + Into + Serialize + Send + Sync, Report: ParallelReporter + Sync, GetData: Fn(&Metadata) -> Data + Copy + Sync, - DataTreeReflection: Into, + DataTreeReflection: Into, { /// Run the sub program. pub fn run(self) -> Result<(), RuntimeError> { @@ -116,11 +116,12 @@ where }; if json_output { - let json_data: JsonData = data_tree + let unit_and_tree: UnitAndTree = data_tree .into_reflection() // I really want to use std::mem::transmute here but can't. .par_convert_names_to_utf8() // TODO: allow non-UTF8 somehow. .expect("convert all names from raw string to UTF-8") .into(); + let json_data = JsonData { unit_and_tree }; return serde_json::to_writer(stdout(), &json_data) .map_err(RuntimeError::SerializationFailure); } diff --git a/src/json_data.rs b/src/json_data.rs index 670b3ff2..75840555 100644 --- a/src/json_data.rs +++ b/src/json_data.rs @@ -7,14 +7,23 @@ use derive_more::{From, TryInto}; #[cfg(feature = "json")] use serde::{Deserialize, Serialize}; -/// Output of the program with `--json-output` flag as well as -/// input of the program with `--json-input` flag. +/// The `"unit"` field and the `"tree"` field of [`JsonData`]. #[derive(Debug, Clone, From, TryInto)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] #[cfg_attr(feature = "json", serde(tag = "unit", content = "tree"))] -pub enum JsonData { +pub enum UnitAndTree { /// Tree where data is [bytes](Bytes). Bytes(Reflection), /// Tree where data is [blocks](Blocks). Blocks(Reflection), } + +/// Output of the program with `--json-output` flag as well as +/// input of the program with `--json-input` flag. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "json", derive(Deserialize, Serialize))] +pub struct JsonData { + /// The `"unit"` field and the `"tree"` field. + #[cfg_attr(feature = "json", serde(flatten))] + pub unit_and_tree: UnitAndTree, +} From fddafe0658e12a10cb968f05b56915a31db47cd4 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sat, 5 Jun 2021 15:29:25 +0700 Subject: [PATCH 2/6] json: Set field names to kebab case --- src/json_data.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/json_data.rs b/src/json_data.rs index 75840555..303f04d1 100644 --- a/src/json_data.rs +++ b/src/json_data.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, From, TryInto)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] #[cfg_attr(feature = "json", serde(tag = "unit", content = "tree"))] +#[cfg_attr(feature = "json", serde(rename_all = "kebab-case"))] pub enum UnitAndTree { /// Tree where data is [bytes](Bytes). Bytes(Reflection), @@ -22,6 +23,7 @@ pub enum UnitAndTree { /// input of the program with `--json-input` flag. #[derive(Debug, Clone)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "kebab-case"))] pub struct JsonData { /// The `"unit"` field and the `"tree"` field. #[cfg_attr(feature = "json", serde(flatten))] From 0f4620bd977ebcfb6461afa91916152fc688fc9f Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sat, 5 Jun 2021 15:34:06 +0700 Subject: [PATCH 3/6] json: Set fields of `Reflection` to kebab case --- src/data_tree/reflection.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data_tree/reflection.rs b/src/data_tree/reflection.rs index b5de7145..cd9d5353 100644 --- a/src/data_tree/reflection.rs +++ b/src/data_tree/reflection.rs @@ -27,6 +27,7 @@ use serde::{Deserialize, Serialize}; /// **Serialization and deserialization:** Requires enabling the `json` feature to enable `serde`. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "kebab-case"))] pub struct Reflection { /// Name of the tree. pub name: Name, From 3243d2d219f9df471db3ece65256db96322b75f5 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sat, 5 Jun 2021 15:58:59 +0700 Subject: [PATCH 4/6] api+json: Create `schema-version` field resolve https://github.com/KSXGitHub/parallel-disk-usage/issues/27 --- src/app/sub.rs | 7 +++-- src/json_data.rs | 6 +++++ src/json_data/schema_version.rs | 46 +++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/json_data/schema_version.rs diff --git a/src/app/sub.rs b/src/app/sub.rs index bc61f06a..a78be53c 100644 --- a/src/app/sub.rs +++ b/src/app/sub.rs @@ -2,7 +2,7 @@ use crate::{ args::Fraction, data_tree::{DataTree, DataTreeReflection}, fs_tree_builder::FsTreeBuilder, - json_data::{JsonData, UnitAndTree}, + json_data::{JsonData, SchemaVersion, UnitAndTree}, os_string_display::OsStringDisplay, reporter::ParallelReporter, runtime_error::RuntimeError, @@ -121,7 +121,10 @@ where .par_convert_names_to_utf8() // TODO: allow non-UTF8 somehow. .expect("convert all names from raw string to UTF-8") .into(); - let json_data = JsonData { unit_and_tree }; + let json_data = JsonData { + schema_version: SchemaVersion, + unit_and_tree, + }; return serde_json::to_writer(stdout(), &json_data) .map_err(RuntimeError::SerializationFailure); } diff --git a/src/json_data.rs b/src/json_data.rs index 303f04d1..4565bd57 100644 --- a/src/json_data.rs +++ b/src/json_data.rs @@ -1,3 +1,7 @@ +pub mod schema_version; + +pub use schema_version::SchemaVersion; + use crate::{ data_tree::Reflection, size::{Blocks, Bytes}, @@ -25,6 +29,8 @@ pub enum UnitAndTree { #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] #[cfg_attr(feature = "json", serde(rename_all = "kebab-case"))] pub struct JsonData { + /// The `"schema-version"` field. + pub schema_version: SchemaVersion, /// The `"unit"` field and the `"tree"` field. #[cfg_attr(feature = "json", serde(flatten))] pub unit_and_tree: UnitAndTree, diff --git a/src/json_data/schema_version.rs b/src/json_data/schema_version.rs new file mode 100644 index 00000000..643f60ed --- /dev/null +++ b/src/json_data/schema_version.rs @@ -0,0 +1,46 @@ +#[cfg(feature = "json")] +use derive_more::Display; +#[cfg(feature = "json")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "json")] +use std::convert::TryFrom; + +/// Content of [`SchemaVersion`]. +pub const SCHEMA_VERSION: &str = "2021-06-05"; + +/// Verifying schema version. +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "json", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "json", serde(try_from = "String", into = "&str"))] +pub struct SchemaVersion; + +/// Error when trying to parse [`SchemaVersion`]. +#[cfg(feature = "json")] +#[derive(Debug, Display)] +#[display( + fmt = "InvalidSchema: {:?}: input schema is not {:?}", + input, + SCHEMA_VERSION +)] +pub struct InvalidSchema { + /// The input string. + pub input: String, +} + +#[cfg(feature = "json")] +impl TryFrom for SchemaVersion { + type Error = InvalidSchema; + fn try_from(input: String) -> Result { + if input == SCHEMA_VERSION { + Ok(SchemaVersion) + } else { + Err(InvalidSchema { input }) + } + } +} + +impl<'a> From for &'a str { + fn from(_: SchemaVersion) -> Self { + SCHEMA_VERSION + } +} From da5a565d2f92014d9cbbdc7f1d3c6127a04f9d90 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sat, 5 Jun 2021 17:03:19 +0700 Subject: [PATCH 5/6] api+json: Create `pdu` field resolve https://github.com/KSXGitHub/parallel-disk-usage/issues/28 --- src/app/sub.rs | 3 ++- src/json_data.rs | 5 +++++ src/json_data/binary_version.rs | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/json_data/binary_version.rs diff --git a/src/app/sub.rs b/src/app/sub.rs index a78be53c..530c8f5b 100644 --- a/src/app/sub.rs +++ b/src/app/sub.rs @@ -2,7 +2,7 @@ use crate::{ args::Fraction, data_tree::{DataTree, DataTreeReflection}, fs_tree_builder::FsTreeBuilder, - json_data::{JsonData, SchemaVersion, UnitAndTree}, + json_data::{BinaryVersion, JsonData, SchemaVersion, UnitAndTree}, os_string_display::OsStringDisplay, reporter::ParallelReporter, runtime_error::RuntimeError, @@ -123,6 +123,7 @@ where .into(); let json_data = JsonData { schema_version: SchemaVersion, + binary_version: Some(BinaryVersion::current()), unit_and_tree, }; return serde_json::to_writer(stdout(), &json_data) diff --git a/src/json_data.rs b/src/json_data.rs index 4565bd57..4c89133c 100644 --- a/src/json_data.rs +++ b/src/json_data.rs @@ -1,5 +1,7 @@ +pub mod binary_version; pub mod schema_version; +pub use binary_version::BinaryVersion; pub use schema_version::SchemaVersion; use crate::{ @@ -31,6 +33,9 @@ pub enum UnitAndTree { pub struct JsonData { /// The `"schema-version"` field. pub schema_version: SchemaVersion, + /// The `"pdu"` field. + #[cfg_attr(feature = "json", serde(rename = "pdu"))] + pub binary_version: Option, /// The `"unit"` field and the `"tree"` field. #[cfg_attr(feature = "json", serde(flatten))] pub unit_and_tree: UnitAndTree, diff --git a/src/json_data/binary_version.rs b/src/json_data/binary_version.rs new file mode 100644 index 00000000..997f8394 --- /dev/null +++ b/src/json_data/binary_version.rs @@ -0,0 +1,19 @@ +use derive_more::{AsMut, AsRef, From, FromStr, Into}; + +#[cfg(feature = "json")] +use serde::{Deserialize, Serialize}; + +/// Version of the current `pdu` program. +pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// Version of the `pdu` program that created the input JSON. +#[derive(Debug, Clone, PartialEq, Eq, AsMut, AsRef, From, FromStr, Into)] +#[cfg_attr(feature = "json", derive(Deserialize, Serialize))] +pub struct BinaryVersion(String); + +impl BinaryVersion { + /// Get version of the current `pdu` program as a `BinaryVersion`. + pub fn current() -> Self { + CURRENT_VERSION.to_string().into() + } +} From 138319559a9f2dbb768c3d4221fd664638b149fc Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sat, 5 Jun 2021 19:59:12 +0700 Subject: [PATCH 6/6] Preserve root paths resolve https://github.com/KSXGitHub/parallel-disk-usage/issues/30 --- src/fs_tree_builder.rs | 3 +- src/lib.rs | 2 - src/utils.rs | 15 ------- src/utils/test_path_name.rs | 82 ------------------------------------- tests/_utils.rs | 8 ++-- 5 files changed, 6 insertions(+), 104 deletions(-) delete mode 100644 src/utils.rs delete mode 100644 src/utils/test_path_name.rs diff --git a/src/fs_tree_builder.rs b/src/fs_tree_builder.rs index 55de73f0..63ccbf7a 100644 --- a/src/fs_tree_builder.rs +++ b/src/fs_tree_builder.rs @@ -4,7 +4,6 @@ use super::{ reporter::{error_report::Operation::*, ErrorReport, Event, Reporter}, size::Size, tree_builder::{Info, TreeBuilder}, - utils::path_name, }; use pipe_trait::Pipe; use std::{ @@ -43,7 +42,7 @@ where } = builder; TreeBuilder:: { - name: path_name(&root), + name: OsStringDisplay::os_string_from(&root), path: root, diff --git a/src/lib.rs b/src/lib.rs index 485bb4c0..dea6f945 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,5 @@ #![deny(warnings)] -mod utils; - #[cfg(feature = "json")] pub use serde; #[cfg(feature = "json")] diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 3e9eed67..00000000 --- a/src/utils.rs +++ /dev/null @@ -1,15 +0,0 @@ -use super::os_string_display::OsStringDisplay; -use std::path::{Component::*, Path}; - -/// Get file name or directory name of a path. -pub fn path_name(path: &Path) -> OsStringDisplay { - match path.components().last() { - None | Some(CurDir) => OsStringDisplay::os_string_from("."), - Some(Normal(name)) => OsStringDisplay::os_string_from(name), - Some(Prefix(prefix)) => OsStringDisplay::os_string_from(prefix.as_os_str()), - Some(RootDir) | Some(ParentDir) => OsStringDisplay::os_string_from(path), - } -} - -#[cfg(test)] -mod test_path_name; diff --git a/src/utils/test_path_name.rs b/src/utils/test_path_name.rs deleted file mode 100644 index 65c0a70b..00000000 --- a/src/utils/test_path_name.rs +++ /dev/null @@ -1,82 +0,0 @@ -use super::path_name; -use crate::os_string_display::OsStringDisplay; -use pretty_assertions::assert_eq; -use std::path::PathBuf; - -#[test] -fn empty() { - let actual = path_name(&PathBuf::new()); - let expected = OsStringDisplay::os_string_from("."); - assert_eq!(actual, expected); -} - -#[test] -fn current_dir() { - let actual = path_name(&PathBuf::from(".")); - let expected = OsStringDisplay::os_string_from("."); - assert_eq!(actual, expected); -} - -#[cfg(unix)] -#[test] -fn root_dir() { - let actual = path_name(&PathBuf::from("/")); - let expected = OsStringDisplay::os_string_from("/"); - assert_eq!(actual, expected); -} - -#[cfg(windows)] -#[test] -fn root_dir() { - let actual = path_name(&PathBuf::from(r"C:\")); - let expected = OsStringDisplay::os_string_from(r"C:\"); - assert_eq!(actual, expected); -} - -#[cfg(windows)] -#[test] -fn prefix() { - let actual = path_name(&PathBuf::from(r"\\prefix")); - let expected = OsStringDisplay::os_string_from("prefix"); - assert_eq!(actual, expected); -} - -#[cfg(unix)] -#[test] -fn normal_relative() { - let actual = path_name(&PathBuf::from("abc/def/ghi")); - let expected = OsStringDisplay::os_string_from("ghi"); - assert_eq!(actual, expected); -} - -#[cfg(unix)] -#[test] -fn normal_absolute() { - let actual = path_name(&PathBuf::from("/abc/def/ghi")); - let expected = OsStringDisplay::os_string_from("ghi"); - assert_eq!(actual, expected); -} - -#[cfg(unix)] -#[test] -fn normal_trailing_separator() { - let actual = path_name(&PathBuf::from("abc/def/ghi/")); - let expected = OsStringDisplay::os_string_from("ghi"); - assert_eq!(actual, expected); -} - -#[cfg(unix)] -#[test] -fn parent_dir() { - let actual = path_name(&PathBuf::from("..")); - let expected = OsStringDisplay::os_string_from(".."); - assert_eq!(actual, expected); -} - -#[cfg(unix)] -#[test] -fn grandparent_dir() { - let actual = path_name(&PathBuf::from("../..")); - let expected = OsStringDisplay::os_string_from("../.."); - assert_eq!(actual, expected); -} diff --git a/tests/_utils.rs b/tests/_utils.rs index 01d56b9c..f94a0bc4 100644 --- a/tests/_utils.rs +++ b/tests/_utils.rs @@ -144,10 +144,12 @@ where .into_reflection() }; + let sub = |suffix: &str| root.join(suffix).pipe(OsStringDisplay::os_string_from); + assert_eq!( measure("flat"), sanitize_tree_reflection(DataTreeReflection { - name: OsStringDisplay::os_string_from("flat"), + name: sub("flat"), data: suffix_size!("flat", "flat/0", "flat/1", "flat/2", "flat/3"), children: vec![ DataTreeReflection { @@ -177,7 +179,7 @@ where assert_eq!( measure("nested"), sanitize_tree_reflection(DataTreeReflection { - name: OsStringDisplay::os_string_from("nested"), + name: sub("nested"), data: suffix_size!("nested", "nested/0", "nested/0/1"), children: vec![DataTreeReflection { name: OsStringDisplay::os_string_from("0"), @@ -194,7 +196,7 @@ where assert_eq!( measure("empty-dir"), sanitize_tree_reflection(DataTreeReflection { - name: OsStringDisplay::os_string_from("empty-dir"), + name: sub("empty-dir"), data: suffix_size!("empty-dir"), children: Vec::new(), }),