From 23f297a7bc8868a162d59d8efac9124ed342735e Mon Sep 17 00:00:00 2001 From: Tom Lienard Date: Thu, 1 Feb 2024 14:27:33 +0000 Subject: [PATCH] feat: preserve JSON indentation (#56) --- Cargo.lock | 55 +++++++++++++++++++++++ Cargo.toml | 1 + src/json.rs | 28 ++++++++++++ src/main.rs | 1 + src/rules/empty_dependencies.rs | 5 ++- src/rules/multiple_dependency_versions.rs | 6 +-- src/rules/non_existant_packages.rs | 5 ++- src/rules/root_package_private_field.rs | 5 ++- src/rules/types_in_dependencies.rs | 5 ++- 9 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 src/json.rs diff --git a/Cargo.lock b/Cargo.lock index 71c4f0b..c915aaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "0.5.0" @@ -189,6 +198,16 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f400d0750c0c069e8493f2256cb4da6f604b6d2eeb69a0ca8863acde352f8400" +[[package]] +name = "detect-indent" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae11867b75e44bacc8baf64be8abe6501c6571bbf33fed819a0a90623c82d1b" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "dyn-clone" version = "1.0.16" @@ -343,6 +362,12 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + [[package]] name = "mio" version = "0.8.9" @@ -414,6 +439,35 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rustix" version = "0.38.13" @@ -498,6 +552,7 @@ dependencies = [ "clap", "colored", "debugless-unwrap", + "detect-indent", "indexmap", "inquire", "insta", diff --git a/Cargo.toml b/Cargo.toml index 5cdc702..6619b95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ readme = "./README.md" anyhow = "1.0.75" clap = { version = "4.4.3", features = ["derive"] } colored = "2.0.4" +detect-indent = "0.1.0" indexmap = { version = "2.0.0", features = ["serde"] } inquire = "0.6.2" semver = "1.0.18" diff --git a/src/json.rs b/src/json.rs new file mode 100644 index 0000000..8d8e163 --- /dev/null +++ b/src/json.rs @@ -0,0 +1,28 @@ +use anyhow::Result; +use detect_indent::{detect_indent, Indent}; +use serde::{Deserialize, Serialize}; +use serde_json::ser::PrettyFormatter; + +pub fn deserialize<'a, T>(value: &'a str) -> Result<(T, Indent)> +where + T: Deserialize<'a>, +{ + let json = serde_json::from_str::(value)?; + let indent = detect_indent(value); + + Ok((json, indent)) +} + +pub fn serialize(value: &T, indent: &Indent) -> Result +where + T: Serialize, +{ + let mut buf = Vec::new(); + let formatter = PrettyFormatter::with_indent(indent.indent().as_bytes()); + let mut serializer = serde_json::Serializer::with_formatter(&mut buf, formatter); + + value.serialize(&mut serializer)?; + let json = String::from_utf8(buf)?; + + Ok(json) +} diff --git a/src/main.rs b/src/main.rs index 502ad63..c40873a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use std::time::Instant; mod args; mod collect; +mod json; mod packages; mod plural; mod printer; diff --git a/src/rules/empty_dependencies.rs b/src/rules/empty_dependencies.rs index 8c8e3f8..d4d76b7 100644 --- a/src/rules/empty_dependencies.rs +++ b/src/rules/empty_dependencies.rs @@ -1,4 +1,5 @@ use super::{Issue, IssueLevel, PackageType}; +use crate::json::{self}; use anyhow::Result; use colored::Colorize; use std::{borrow::Cow, fmt::Display, fs, path::PathBuf}; @@ -71,7 +72,7 @@ impl Issue for EmptyDependenciesIssue { if let PackageType::Package(path) = package_type { let path = PathBuf::from(path).join("package.json"); let value = fs::read_to_string(&path)?; - let mut value = serde_json::from_str::(&value)?; + let (mut value, indent) = json::deserialize::(&value)?; let dependency = self.dependency_kind.to_string(); if let Some(dependency_field) = value.get(&dependency) { @@ -79,7 +80,7 @@ impl Issue for EmptyDependenciesIssue { { value.as_object_mut().unwrap().remove(&dependency); - let value = serde_json::to_string_pretty(&value)?; + let value = json::serialize(&value, &indent)?; fs::write(path, value)?; self.fixed = true; diff --git a/src/rules/multiple_dependency_versions.rs b/src/rules/multiple_dependency_versions.rs index 06d6c8f..a021e7b 100644 --- a/src/rules/multiple_dependency_versions.rs +++ b/src/rules/multiple_dependency_versions.rs @@ -1,5 +1,5 @@ use super::{Issue, IssueLevel, PackageType}; -use crate::packages::semversion::SemVersion; +use crate::{json, packages::semversion::SemVersion}; use anyhow::Result; use colored::Colorize; use indexmap::IndexMap; @@ -144,7 +144,7 @@ impl Issue for MultipleDependencyVersionsIssue { for package in self.versions.keys() { let path = PathBuf::from(package).join("package.json"); let value = fs::read_to_string(&path)?; - let mut value = serde_json::from_str::(&value)?; + let (mut value, indent) = json::deserialize::(&value)?; if let Some(dependencies) = value.get_mut("dependencies") { let dependencies = dependencies.as_object_mut().unwrap(); @@ -162,7 +162,7 @@ impl Issue for MultipleDependencyVersionsIssue { } } - let value = serde_json::to_string_pretty(&value)?; + let value = json::serialize(&value, &indent)?; fs::write(path, value)?; } diff --git a/src/rules/non_existant_packages.rs b/src/rules/non_existant_packages.rs index cae4e80..1d90c96 100644 --- a/src/rules/non_existant_packages.rs +++ b/src/rules/non_existant_packages.rs @@ -1,4 +1,5 @@ use super::{Issue, IssueLevel, PackageType}; +use crate::json; use anyhow::Result; use colored::Colorize; use std::{borrow::Cow, fs, path::PathBuf}; @@ -127,7 +128,7 @@ impl Issue for NonExistantPackagesIssue { false => { let path = PathBuf::from("package.json"); let value = fs::read_to_string(&path)?; - let mut value = serde_json::from_str::(&value)?; + let (mut value, indent) = json::deserialize::(&value)?; value .get_mut("workspaces") @@ -140,7 +141,7 @@ impl Issue for NonExistantPackagesIssue { !self.paths.contains(&package) }); - let value = serde_json::to_string_pretty(&value)?; + let value = json::serialize(&value, &indent)?; fs::write(path, value)?; self.fixed = true; diff --git a/src/rules/root_package_private_field.rs b/src/rules/root_package_private_field.rs index ea7bf75..0b2224e 100644 --- a/src/rules/root_package_private_field.rs +++ b/src/rules/root_package_private_field.rs @@ -1,4 +1,5 @@ use super::{Issue, IssueLevel, PackageType}; +use crate::json; use anyhow::Result; use colored::Colorize; use std::{borrow::Cow, fs, path::PathBuf}; @@ -48,14 +49,14 @@ impl Issue for RootPackagePrivateFieldIssue { if let PackageType::Root = package_type { let path = PathBuf::from("package.json"); let value = fs::read_to_string(&path)?; - let mut value = serde_json::from_str::(&value)?; + let (mut value, indent) = json::deserialize::(&value)?; value .as_object_mut() .unwrap() .insert("private".to_string(), serde_json::Value::Bool(true)); - let value = serde_json::to_string_pretty(&value)?; + let value = json::serialize(&value, &indent)?; fs::write(path, value)?; self.fixed = true; diff --git a/src/rules/types_in_dependencies.rs b/src/rules/types_in_dependencies.rs index 7a1e1b4..5deed50 100644 --- a/src/rules/types_in_dependencies.rs +++ b/src/rules/types_in_dependencies.rs @@ -1,4 +1,5 @@ use super::{Issue, IssueLevel, PackageType}; +use crate::json; use anyhow::Result; use colored::Colorize; use indexmap::IndexMap; @@ -84,7 +85,7 @@ impl Issue for TypesInDependenciesIssue { if let PackageType::Package(path) = package_type { let path = PathBuf::from(path).join("package.json"); let value = fs::read_to_string(&path)?; - let mut value = serde_json::from_str::(&value)?; + let (mut value, indent) = json::deserialize::(&value)?; let dependencies = value .get_mut("dependencies") @@ -118,7 +119,7 @@ impl Issue for TypesInDependenciesIssue { dev_dependencies.insert(package, version); } - let value = serde_json::to_string_pretty(&value)?; + let value = json::serialize(&value, &indent)?; fs::write(path, value)?; self.fixed = true;