From ae61ecfaa95f45227e00c79c11e1b3112a8e1db4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 17:39:41 +0000 Subject: [PATCH] lint: upgrade `perfectionist` to `0.0.0-rc.18` Bump the dylint library tag from `0.0.0-rc.17` to `0.0.0-rc.18` and bring the tree into compliance with the rules it adds or extends: - `import_grouping` (new, active): configured for the `single_group` style. Blank lines that split an import run are removed so each module keeps one contiguous `use` block. - `bare_identifier_reference` (new, active): backticked identifiers in doc comments that resolve in scope are now written as intra-doc links. - `prefer_expect_over_allow` (new, active): the `clippy::float_cmp` suppressions in the fraction tests switch from `#[allow]` to `#[expect]`. - `prefer_raw_string` (broadened in KSXGitHub/perfectionist#233): the rule now scans macro arguments, so the roff `write!`/`format!` strings in `man_page` become raw strings. The rendered man page is byte-for-byte unchanged. The `single_group` style cannot keep `#[cfg]`-gated imports in their own trailing group, so files that rely on that layout carry a module-level `#[expect(perfectionist::import_grouping)]`. This limitation is tracked in #436. https://claude.ai/code/session_016ZyYFnzSv876usUHLEX4qe --- CONTRIBUTING.md | 19 ++++++++++-- dylint.toml | 5 +++- src/app.rs | 11 +++++-- src/app/hdd.rs | 8 ++++++ src/app/hdd/test_linux.rs | 6 ++-- src/args.rs | 9 +++--- src/bytes_format.rs | 9 +++++- src/data_tree.rs | 12 ++++---- src/data_tree/reflection.rs | 24 ++++++++++------ src/device.rs | 8 ++++++ src/get_size.rs | 8 ++++++ src/hardlink/hardlink_list.rs | 22 ++++++++------ src/hardlink/hardlink_list/reflection.rs | 14 +++++++-- src/hardlink/hardlink_list/summary.rs | 8 ++++++ src/hardlink/link_path_list.rs | 6 ++-- src/hardlink/link_path_list/reflection.rs | 14 +++++++-- src/inode.rs | 8 ++++++ src/json_data.rs | 13 +++++++-- src/json_data/binary_version.rs | 10 ++++++- src/man_page.rs | 32 ++++++++++----------- src/os_string_display.rs | 8 ++++++ src/reporter.rs | 3 +- src/reporter/error_report.rs | 1 - src/size.rs | 8 ++++++ src/tree_builder.rs | 5 ++-- src/visualizer.rs | 7 ++--- src/visualizer/methods.rs | 9 +++--- tests/args_fraction.rs | 6 ++-- tests/cli_errors.rs | 10 ++++++- tests/flag_combinations.rs | 1 - tests/fs_tree_builder.rs | 11 ++++++- tests/hardlinks_deduplication.rs | 1 - tests/hardlinks_deduplication_multi_args.rs | 1 - tests/hardlinks_without_deduplication.rs | 1 - tests/json.rs | 1 - tests/one_file_system.rs | 1 - tests/usual_cli.rs | 10 ++++++- 37 files changed, 235 insertions(+), 95 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7dc9e0da..e8a9b96c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,11 +36,24 @@ Automated tools enforce formatting (`cargo fmt`), linting (`cargo clippy`), and ### Import Organization -Import granularity is enforced automatically by the `perfectionist::import_granularity` rule, configured for the `module` style. Items from the same module are merged into a single braced `use` statement, while each module keeps its own `use` statement rather than collapsing an entire crate into one nested-braces statement. Import ordering is enforced separately by `cargo fmt`. +Two `perfectionist` rules govern imports automatically: -The remaining convention is not enforced and must be applied by hand. Imports gated by a platform attribute such as `#[cfg(unix)]` go in a separate block after the main imports. +- `perfectionist::import_granularity`, configured for the `module` style, controls how items are merged within each `use` statement. Items from the same module are merged into a single braced `use` statement, while each module keeps its own `use` statement rather than collapsing an entire crate into one nested-braces statement. +- `perfectionist::import_grouping`, configured for the `single_group` style, controls how `use` statements are partitioned into blocks. Every `use` statement, whether a `pub use` re-export or a private import, sits in one contiguous block with no blank lines between them. + +Import ordering within the block is enforced separately by `cargo fmt`. + +Imports gated by a platform or feature attribute such as `#[cfg(unix)]` are kept in their own block after the main imports, separated by a blank line. The `single_group` style cannot yet express this exception, so each file that uses it carries a module-level suppression. See [issue #436](https://github.com/KSXGitHub/parallel-disk-usage/issues/436). ```rust +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use crate::args::{Args, Quantity, Threads}; use crate::bytes_format::BytesFormat; use crate::size; @@ -57,7 +70,7 @@ use crate::get_size::{GetBlockCount, GetBlockSize}; The flat file pattern (`module.rs` rather than `module/mod.rs`) is enforced by the `perfectionist::flat_module_pattern` dylint check. In addition to that requirement, follow these conventions: -- List `pub mod` declarations first, then `pub use` re-exports, then private imports and items. +- List `pub mod` declarations first, followed by the `use` block, then the remaining items. The `use` block holds both `pub use` re-exports and private imports; `cargo fmt` and `perfectionist::import_grouping` keep them in one sorted group, so do not rely on a fixed order between the two kinds. - Use `pub use` to re-export key types at the module level for convenience. ```rust diff --git a/dylint.toml b/dylint.toml index 36185081..179c3918 100644 --- a/dylint.toml +++ b/dylint.toml @@ -1,6 +1,6 @@ [workspace.metadata.dylint] libraries = [ - { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.17" }, + { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.18" }, ] ["perfectionist::derive_ordering"] @@ -22,6 +22,9 @@ prefix = [ ["perfectionist::import_granularity"] style = "module" +["perfectionist::import_grouping"] +style = "single_group" + ["perfectionist::macro_argument_binding"] deny_extra = ["debug_assert_op", "debug_assert_op_expr"] allow_extra = ["assert_op_expr"] diff --git a/src/app.rs b/src/app.rs index d98cdc2e..7c1b5a19 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,6 +1,12 @@ -pub mod sub; +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] -pub use sub::Sub; +pub mod sub; use crate::args::{Args, Quantity, Threads}; use crate::bytes_format::BytesFormat; @@ -17,6 +23,7 @@ use pipe_trait::Pipe; use std::io::stdin; use std::time::Duration; use sub::JsonOutputParam; +pub use sub::Sub; use sysinfo::{Disk, Disks}; #[cfg(unix)] diff --git a/src/app/hdd.rs b/src/app/hdd.rs index e93567d7..c941e16a 100644 --- a/src/app/hdd.rs +++ b/src/app/hdd.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use super::mount_point::find_mount_point; use std::ffi::OsStr; use std::fs::canonicalize; diff --git a/src/app/hdd/test_linux.rs b/src/app/hdd/test_linux.rs index 5ce270bf..90055a2c 100644 --- a/src/app/hdd/test_linux.rs +++ b/src/app/hdd/test_linux.rs @@ -39,8 +39,8 @@ fn test_parse_block_device_name() { } } -/// Generate a test that builds a mock `FsApi` with identity `canonicalize`, -/// then asserts that `reclassify_virtual_hdd` maps `DiskKind::HDD` to the +/// Generate a test that builds a mock [`FsApi`] with identity `canonicalize`, +/// then asserts that [`reclassify_virtual_hdd`] maps `DiskKind::HDD` to the /// expected `DiskKind`. /// /// The sysfs paths (`/sys/block/{block}` and @@ -171,7 +171,7 @@ identity_reclassify_test_case! { /// /// **Note:** On real LVM setups, `/dev/mapper/vg0-lv0` canonicalizes to /// `/dev/dm-0`, not a partition device. See -/// `test_mapper_dm_device_is_not_corrected` for that case. +/// [`test_mapper_dm_device_is_not_corrected`] for that case. #[test] fn test_mapper_symlink_resolves_to_virtual_partition() { struct Fs; diff --git a/src/args.rs b/src/args.rs index a1e8961d..9bd4b2e5 100644 --- a/src/args.rs +++ b/src/args.rs @@ -3,19 +3,18 @@ pub mod fraction; pub mod quantity; pub mod threads; -pub use depth::Depth; -pub use fraction::Fraction; -pub use quantity::Quantity; -pub use threads::Threads; - use crate::bytes_format::BytesFormat; use crate::visualizer::ColumnWidthDistribution; use clap::{ColorChoice, Parser}; +pub use depth::Depth; use derive_setters::Setters; +pub use fraction::Fraction; +pub use quantity::Quantity; use smart_default::SmartDefault; use std::path::PathBuf; use terminal_size::{Width, terminal_size}; use text_block_macros::text_block; +pub use threads::Threads; /// The CLI arguments. #[derive(Debug, SmartDefault, Clone, Parser, Setters)] diff --git a/src/bytes_format.rs b/src/bytes_format.rs index 8d92d19b..802c0b49 100644 --- a/src/bytes_format.rs +++ b/src/bytes_format.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + pub mod formatter; pub mod output; pub mod parsed_value; @@ -6,7 +14,6 @@ pub mod scale_base; pub use formatter::Formatter; pub use output::Output; pub use parsed_value::ParsedValue; - use pipe_trait::Pipe; #[cfg(feature = "cli")] diff --git a/src/data_tree.rs b/src/data_tree.rs index 6c68ff6e..dbc22b14 100644 --- a/src/data_tree.rs +++ b/src/data_tree.rs @@ -1,14 +1,12 @@ pub mod reflection; -pub use reflection::Reflection; - -pub use Reflection as DataTreeReflection; - use super::size; +pub use Reflection as DataTreeReflection; +pub use reflection::Reflection; /// Disk usage data of a filesystem tree. /// -/// **Construction:** There are 3 main ways to create a `DataTree`: +/// **Construction:** There are 3 main ways to create a [`DataTree`]: /// * Use [`FsTreeBuilder`](crate::fs_tree_builder::FsTreeBuilder) to create it from the real /// filesystem. /// * Use [`TreeBuilder`](crate::tree_builder::TreeBuilder) to create it from a representation @@ -16,9 +14,9 @@ use super::size; /// * Use [`Reflection`]. /// /// **Visualization:** Use the [`Visualizer`](crate::visualizer::Visualizer) struct to create an -/// ASCII chart that visualizes `DataTree`. +/// ASCII chart that visualizes [`DataTree`]. /// -/// **Serialization and deserialization:** _(feature: `json`)_ `DataTree` does not implement +/// **Serialization and deserialization:** _(feature: `json`)_ [`DataTree`] does not implement /// `Serialize` and `Deserialize` traits directly, instead, it can be converted into/from a /// [`Reflection`] which implements these traits. #[derive(Debug, PartialEq, Eq)] diff --git a/src/data_tree/reflection.rs b/src/data_tree/reflection.rs index 47b64666..e6cbdb59 100644 --- a/src/data_tree/reflection.rs +++ b/src/data_tree/reflection.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use crate::size; use std::collections::VecDeque; use std::ffi::OsStr; @@ -10,21 +18,21 @@ use serde::{Deserialize, Serialize}; /// Intermediate format used for construction and inspection of /// [`DataTree`](crate::data_tree::DataTree)'s internal content. /// -/// Unlike `DataTree` where the fields are all private, the fields of `Reflection` +/// Unlike `DataTree` where the fields are all private, the fields of [`Reflection`] /// are all public to allow construction in tests. /// -/// **Conversion between `DataTree` and `Reflection`:** -/// * Any `DataTree` can be safely [transmuted](std::mem::transmute) to a valid `Reflection`. -/// * Any `Reflection` can be safely transmuted to a potentially invalid `DataTree`. -/// * To safely convert a `DataTree` into a `Reflection` without the `unsafe` keyword, use +/// **Conversion between `DataTree` and [`Reflection`]:** +/// * Any `DataTree` can be safely [transmuted](std::mem::transmute) to a valid [`Reflection`]. +/// * Any [`Reflection`] can be safely transmuted to a potentially invalid `DataTree`. +/// * To safely convert a `DataTree` into a [`Reflection`] without the `unsafe` keyword, use /// [`DataTree::into_reflection`](crate::data_tree::DataTree::into_reflection) /// (it would be slower than using `transmute`). -/// * To safely convert a `Reflection` into a valid `DataTree`, +/// * To safely convert a [`Reflection`] into a valid `DataTree`, /// use [`par_try_into_tree`](Self::par_try_into_tree). /// -/// **Serialization and deserialization:** _(feature: `json`)_ `Reflection` implements +/// **Serialization and deserialization:** _(feature: `json`)_ [`Reflection`] implements /// `Serialize` and `Deserialize` traits, this allows functions in `serde_json` to convert -/// a `Reflection` into/from JSON. +/// a [`Reflection`] into/from JSON. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] #[cfg_attr(feature = "json", serde(rename_all = "kebab-case"))] diff --git a/src/device.rs b/src/device.rs index 8b43d9ba..6d6a675d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use derive_more::{Display, From, Into, LowerHex, Octal, UpperHex}; #[cfg(feature = "json")] diff --git a/src/get_size.rs b/src/get_size.rs index 235fef8c..a858a2a3 100644 --- a/src/get_size.rs +++ b/src/get_size.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use super::size::Bytes; use std::fs::Metadata; diff --git a/src/hardlink/hardlink_list.rs b/src/hardlink/hardlink_list.rs index 4846c83f..04ed9bcf 100644 --- a/src/hardlink/hardlink_list.rs +++ b/src/hardlink/hardlink_list.rs @@ -1,22 +1,28 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + pub mod iter; pub mod reflection; pub mod summary; -pub use iter::Iter; -pub use reflection::Reflection; -pub use summary::Summary; - -pub use Reflection as HardlinkListReflection; -pub use Summary as SharedLinkSummary; - use crate::device::DeviceNumber; use crate::hardlink::LinkPathList; use crate::inode::InodeNumber; use crate::size; +pub use Reflection as HardlinkListReflection; +pub use Summary as SharedLinkSummary; use dashmap::DashMap; use derive_more::{Display, Error}; +pub use iter::Iter; +pub use reflection::Reflection; use smart_default::SmartDefault; use std::fmt::Debug; +pub use summary::Summary; #[cfg(any(unix, test))] use pipe_trait::Pipe; @@ -45,7 +51,7 @@ struct Value { /// Storage to be used by [`crate::hardlink::RecordHardlinks`]. /// -/// **Reflection:** `HardlinkList` does not implement `PartialEq`, `Eq`, +/// **Reflection:** [`HardlinkList`] does not implement `PartialEq`, `Eq`, /// `Deserialize`, and `Serialize` directly. Instead, it can be converted into a /// [`Reflection`] which implement these traits. #[derive(Debug, SmartDefault, Clone)] diff --git a/src/hardlink/hardlink_list/reflection.rs b/src/hardlink/hardlink_list/reflection.rs index 33df41d6..ab1aa91a 100644 --- a/src/hardlink/hardlink_list/reflection.rs +++ b/src/hardlink/hardlink_list/reflection.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use super::{HardlinkList, InodeKey, Value}; use crate::device::DeviceNumber; use crate::hardlink::LinkPathListReflection; @@ -17,11 +25,11 @@ use serde::{Deserialize, Serialize}; /// * Every pair of an inode number and a device number is unique. /// * The internal list is always sorted by pairs of an inode number and a device number. /// -/// **Equality:** `Reflection` implements `PartialEq` and `Eq` traits. +/// **Equality:** [`Reflection`] implements `PartialEq` and `Eq` traits. /// -/// **Serialization and deserialization:** _(feature: `json`)_ `Reflection` implements +/// **Serialization and deserialization:** _(feature: `json`)_ [`Reflection`] implements /// `Serialize` and `Deserialize` traits, this allows functions in `serde_json` to convert -/// a `Reflection` into/from JSON. +/// a [`Reflection`] into/from JSON. #[derive(Debug, Clone, PartialEq, Eq, Into, IntoIterator)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] pub struct Reflection(Vec>); diff --git a/src/hardlink/hardlink_list/summary.rs b/src/hardlink/hardlink_list/summary.rs index 22fabf11..b4d6d1b7 100644 --- a/src/hardlink/hardlink_list/summary.rs +++ b/src/hardlink/hardlink_list/summary.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use super::iter::Item as IterItem; use super::reflection::ReflectionEntry; use super::{HardlinkList, Reflection}; diff --git a/src/hardlink/link_path_list.rs b/src/hardlink/link_path_list.rs index e13b1d44..a8276471 100644 --- a/src/hardlink/link_path_list.rs +++ b/src/hardlink/link_path_list.rs @@ -1,16 +1,14 @@ mod iter; mod reflection; +pub use Reflection as LinkPathListReflection; pub use iter::Iter; pub use reflection::Reflection; - -pub use Reflection as LinkPathListReflection; - use std::path::PathBuf; /// List of different hardlinks to the same file. /// -/// **Reflection:** `LinkPathList` does not implement `PartialEq`, `Eq`, +/// **Reflection:** [`LinkPathList`] does not implement `PartialEq`, `Eq`, /// `Deserialize`, and `Serialize` directly. Instead, it can be converted into a /// [`Reflection`] which implement these traits. Do note that the time complexity /// of such conversion is O(n) as it has to convert a `Vec` into a `HashSet`. diff --git a/src/hardlink/link_path_list/reflection.rs b/src/hardlink/link_path_list/reflection.rs index 23c50b54..221c22d0 100644 --- a/src/hardlink/link_path_list/reflection.rs +++ b/src/hardlink/link_path_list/reflection.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use super::LinkPathList; use derive_more::{From, Into, IntoIterator}; use pipe_trait::Pipe; @@ -10,11 +18,11 @@ use serde::{Deserialize, Serialize}; /// Intermediate format used for construction and inspection of [`LinkPathList`]'s /// internal content. /// -/// **Equality:** `Reflection` implements `PartialEq` and `Eq` traits. +/// **Equality:** [`Reflection`] implements `PartialEq` and `Eq` traits. /// -/// **Serialization and deserialization:** _(feature: `json`)_ `Reflection` implements +/// **Serialization and deserialization:** _(feature: `json`)_ [`Reflection`] implements /// `Serialize` and `Deserialize` traits, this allows functions in `serde_json` to convert -/// a `Reflection` into/from JSON. +/// a [`Reflection`] into/from JSON. #[derive(Debug, Default, Clone, PartialEq, Eq, From, Into, IntoIterator)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] pub struct Reflection(pub HashSet); diff --git a/src/inode.rs b/src/inode.rs index 3842ad51..94b6d7b0 100644 --- a/src/inode.rs +++ b/src/inode.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use derive_more::{Display, From, Into, LowerHex, Octal, UpperHex}; #[cfg(feature = "json")] diff --git a/src/json_data.rs b/src/json_data.rs index 3f0ec680..c7d81916 100644 --- a/src/json_data.rs +++ b/src/json_data.rs @@ -1,13 +1,20 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + pub mod binary_version; pub mod schema_version; -pub use binary_version::BinaryVersion; -pub use schema_version::SchemaVersion; - use crate::data_tree::DataTreeReflection; use crate::hardlink::{HardlinkListReflection, SharedLinkSummary}; use crate::size::{self, Blocks, Bytes}; +pub use binary_version::BinaryVersion; use derive_more::{Deref, DerefMut, From, TryInto}; +pub use schema_version::SchemaVersion; use smart_default::SmartDefault; #[cfg(feature = "json")] diff --git a/src/json_data/binary_version.rs b/src/json_data/binary_version.rs index 1fcb077a..38cf014e 100644 --- a/src/json_data/binary_version.rs +++ b/src/json_data/binary_version.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use derive_more::{AsMut, AsRef, From, FromStr, Into}; #[cfg(feature = "json")] @@ -12,7 +20,7 @@ pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub struct BinaryVersion(String); impl BinaryVersion { - /// Get version of the current `pdu` program as a `BinaryVersion`. + /// Get version of the current `pdu` program as a [`BinaryVersion`]. #[inline] pub fn current() -> Self { CURRENT_VERSION.to_string().into() diff --git a/src/man_page.rs b/src/man_page.rs index e332d788..20e512e8 100644 --- a/src/man_page.rs +++ b/src/man_page.rs @@ -58,7 +58,7 @@ fn resolve_flag_name(command: &Command, arg_id: &str) -> Option { .get_arguments() .find(|arg| arg.get_id().as_str() == arg_id) .and_then(|arg| arg.get_long()) - .map(|long| format!("\\fB\\-\\-{}\\fR", roff_escape(long))) + .map(|long| format!(r"\fB\-\-{}\fR", roff_escape(long))) } /// Escapes a string for roff by replacing hyphens with `\-`. @@ -69,7 +69,7 @@ fn roff_escape(text: &str) -> String { fn render_title(out: &mut String, command: &Command) { let name = command.get_name(); let version = command.get_version().unwrap_or_default(); - writeln!(out, ".TH {name} 1 \"{name} {version}\"").unwrap(); + writeln!(out, r#".TH {name} 1 "{name} {version}""#).unwrap(); } fn render_name_section(out: &mut String, command: &Command) { @@ -79,12 +79,12 @@ fn render_name_section(out: &mut String, command: &Command) { .map(ToString::to_string) .unwrap_or_default(); writeln!(out, ".SH NAME").unwrap(); - writeln!(out, "{name} \\- {}", roff_escape(&about)).unwrap(); + writeln!(out, r"{name} \- {}", roff_escape(&about)).unwrap(); } fn render_synopsis_section(out: &mut String, command: &Command) { out.push_str(".SH SYNOPSIS\n"); - write!(out, "\\fB{}\\fR", command.get_name()).unwrap(); + write!(out, r"\fB{}\fR", command.get_name()).unwrap(); let options = command .get_arguments() .filter(|arg| !arg.is_positional()) @@ -107,19 +107,19 @@ fn render_synopsis_section(out: &mut String, command: &Command) { fn render_synopsis_option(out: &mut String, arg: &Arg) { out.push('['); if let Some(short) = arg.get_short() { - write!(out, "\\fB\\-{}\\fR", roff_escape(&short.to_string())).unwrap(); + write!(out, r"\fB\-{}\fR", roff_escape(&short.to_string())).unwrap(); if arg.get_long().is_some() { out.push('|'); } } if let Some(long) = arg.get_long() { - write!(out, "\\fB\\-\\-{}\\fR", roff_escape(long)).unwrap(); + write!(out, r"\fB\-\-{}\fR", roff_escape(long)).unwrap(); } if arg.get_action().takes_values() && let Some(value_names) = arg.get_value_names() { for name in value_names { - write!(out, " \\fI{}\\fR", roff_escape(name)).unwrap(); + write!(out, r" \fI{}\fR", roff_escape(name)).unwrap(); } } out.push(']'); @@ -139,9 +139,9 @@ fn render_synopsis_positional(out: &mut String, arg: &Arg) { .unwrap_or_else(|| arg.get_id().as_str()); let ellipsis = if is_multiple(arg) { "..." } else { "" }; if arg.is_required_set() { - write!(out, "\\fI{}\\fR{ellipsis}", roff_escape(name)).unwrap(); + write!(out, r"\fI{}\fR{ellipsis}", roff_escape(name)).unwrap(); } else { - write!(out, "[\\fI{}\\fR]{ellipsis}", roff_escape(name)).unwrap(); + write!(out, r"[\fI{}\fR]{ellipsis}", roff_escape(name)).unwrap(); } } @@ -213,9 +213,9 @@ fn render_option_header_positional(out: &mut String, arg: &Arg) { .unwrap_or_else(|| arg.get_id().as_str()); let ellipsis = if is_multiple(arg) { "..." } else { "" }; if arg.is_required_set() { - writeln!(out, "\\fI{name}\\fR{ellipsis}").unwrap(); + writeln!(out, r"\fI{name}\fR{ellipsis}").unwrap(); } else { - writeln!(out, "[\\fI{name}\\fR]{ellipsis}").unwrap(); + writeln!(out, r"[\fI{name}\fR]{ellipsis}").unwrap(); } } @@ -223,17 +223,17 @@ fn render_option_header_flag(out: &mut String, arg: &Arg) { let short = arg .get_short() .map(|short| roff_escape(&short.to_string())) - .map(|short| format!("\\fB\\-{short}\\fR")); + .map(|short| format!(r"\fB\-{short}\fR")); let long = arg .get_long() .map(roff_escape) - .map(|long| format!("\\fB\\-\\-{long}\\fR")); + .map(|long| format!(r"\fB\-\-{long}\fR")); let aliases = arg .get_visible_aliases() .into_iter() .flatten() .map(roff_escape) - .map(|alias| format!("\\fB\\-\\-{alias}\\fR")); + .map(|alias| format!(r"\fB\-\-{alias}\fR")); let header = short.into_iter().chain(long).chain(aliases).join(", "); if arg.get_action().takes_values() { let value_str = render_value_hint(arg); @@ -252,7 +252,7 @@ fn render_value_hint(arg: &Arg) -> String { .unwrap_or_else(|| vec![arg.get_id().as_str()]) .into_iter() .map(roff_escape) - .map(|name| format!("\\fI<{name}>\\fR")) + .map(|name| format!(r"\fI<{name}>\fR")) .join(" "); let defaults = arg .get_default_values() @@ -291,7 +291,7 @@ fn render_possible_values(out: &mut String, arg: &Arg) { let flag = arg .get_long() .map(roff_escape) - .map(|long| format!("\\-\\-{long}")) + .map(|long| format!(r"\-\-{long}")) .unwrap_or_default(); out.push_str(".RS\n"); for value in &possible_values { diff --git a/src/os_string_display.rs b/src/os_string_display.rs index 30950a14..c74088e4 100644 --- a/src/os_string_display.rs +++ b/src/os_string_display.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use derive_more::{AsMut, AsRef, Deref, DerefMut, From, FromStr}; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Display, Error, Formatter}; diff --git a/src/reporter.rs b/src/reporter.rs index b1663fe4..1ffd90e2 100644 --- a/src/reporter.rs +++ b/src/reporter.rs @@ -4,14 +4,13 @@ pub mod event; pub mod progress_and_error_reporter; pub mod progress_report; +use crate::size; pub use error_only_reporter::ErrorOnlyReporter; pub use error_report::ErrorReport; pub use event::Event; pub use progress_and_error_reporter::ProgressAndErrorReporter; pub use progress_report::ProgressReport; -use crate::size; - /// Report progress. pub trait Reporter { /// Handle report event. diff --git a/src/reporter/error_report.rs b/src/reporter/error_report.rs index ef6bd2b6..4c578b76 100644 --- a/src/reporter/error_report.rs +++ b/src/reporter/error_report.rs @@ -1,7 +1,6 @@ pub mod operation; pub use operation::Operation; - use std::io::Error; use std::path::Path; diff --git a/src/size.rs b/src/size.rs index bb0fb893..a62f361c 100644 --- a/src/size.rs +++ b/src/size.rs @@ -1,3 +1,11 @@ +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + use super::bytes_format::{self, BytesFormat}; use derive_more::{Add, AddAssign, From, Into, Sub, SubAssign, Sum}; use std::fmt::{Debug, Display}; diff --git a/src/tree_builder.rs b/src/tree_builder.rs index d2111bf4..85e1285e 100644 --- a/src/tree_builder.rs +++ b/src/tree_builder.rs @@ -1,9 +1,8 @@ pub mod info; -pub use info::Info; - use super::data_tree::DataTree; use super::size; +pub use info::Info; use rayon::prelude::*; /// Collection of functions and starting points in order to build a [`DataTree`] with [`From`] or [`Into`]. @@ -20,7 +19,7 @@ where pub path: Path, /// Name of the root. pub name: Name, - /// Function to extract necessary information from `path` (`size` and `children`). + /// Function to extract necessary information from `path` ([`size`] and `children`). pub get_info: GetInfo, /// Function to join parent's `path` with a child's name to make the child's `name`. pub join_path: JoinPath, diff --git a/src/visualizer.rs b/src/visualizer.rs index ccc9eb62..7e67b220 100644 --- a/src/visualizer.rs +++ b/src/visualizer.rs @@ -6,17 +6,16 @@ pub mod parenthood; pub mod proportion_bar; pub mod tree; +use super::data_tree::DataTree; +use super::size; pub use bar_alignment::BarAlignment; pub use child_position::ChildPosition; pub use column_width_distribution::ColumnWidthDistribution; pub use direction::Direction; pub use parenthood::Parenthood; pub use proportion_bar::{ProportionBar, ProportionBarBlock}; -pub use tree::{TreeHorizontalSlice, TreeSkeletalComponent}; - -use super::data_tree::DataTree; -use super::size; use std::fmt::Display; +pub use tree::{TreeHorizontalSlice, TreeSkeletalComponent}; /// Visualize a [`DataTree`]. /// diff --git a/src/visualizer/methods.rs b/src/visualizer/methods.rs index 725c369f..4361aa22 100644 --- a/src/visualizer/methods.rs +++ b/src/visualizer/methods.rs @@ -5,17 +5,16 @@ mod node_info; mod table; mod tree_table; +use super::{ColumnWidthDistribution, Visualizer}; +use crate::size; use bar_table::*; use constants::*; use initial_table::*; use node_info::*; -use table::*; -use tree_table::*; - -use super::{ColumnWidthDistribution, Visualizer}; -use crate::size; use std::cmp::min; use std::fmt::Display; +use table::*; +use tree_table::*; use zero_copy_pads::{align_left, align_right}; impl<'a, Name, Size> Visualizer<'a, Name, Size> diff --git a/tests/args_fraction.rs b/tests/args_fraction.rs index 326341d4..31f333b9 100644 --- a/tests/args_fraction.rs +++ b/tests/args_fraction.rs @@ -6,7 +6,7 @@ use parallel_disk_usage::args::fraction::FromStrError::*; use pretty_assertions::assert_eq; #[test] -#[allow( +#[expect( clippy::float_cmp, reason = "the parsed fraction is expected to equal the source literal exactly" )] @@ -16,7 +16,7 @@ fn typical() { } #[test] -#[allow( +#[expect( clippy::float_cmp, reason = "the parsed fraction is expected to equal the source literal exactly" )] @@ -38,7 +38,7 @@ fn less_than_zero() { } #[test] -#[allow( +#[expect( clippy::float_cmp, reason = "the parsed fraction is expected to equal the source literal exactly" )] diff --git a/tests/cli_errors.rs b/tests/cli_errors.rs index b5ca4b20..854d8cc8 100644 --- a/tests/cli_errors.rs +++ b/tests/cli_errors.rs @@ -1,8 +1,16 @@ #![cfg(feature = "cli")] +#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] +#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] pub mod _utils; pub use _utils::*; - use command_extra::CommandExtra; use pipe_trait::Pipe; use pretty_assertions::assert_eq; diff --git a/tests/flag_combinations.rs b/tests/flag_combinations.rs index 70c1c6b8..c5584963 100644 --- a/tests/flag_combinations.rs +++ b/tests/flag_combinations.rs @@ -2,7 +2,6 @@ pub mod _utils; pub use _utils::*; - use command_extra::CommandExtra; use std::process::Stdio; diff --git a/tests/fs_tree_builder.rs b/tests/fs_tree_builder.rs index 3401b328..d588c140 100644 --- a/tests/fs_tree_builder.rs +++ b/tests/fs_tree_builder.rs @@ -1,6 +1,15 @@ +#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] +#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] + pub mod _utils; pub use _utils::*; - use parallel_disk_usage::get_size::GetApparentSize; use parallel_disk_usage::size::Bytes; diff --git a/tests/hardlinks_deduplication.rs b/tests/hardlinks_deduplication.rs index 201789e7..fd2737fe 100644 --- a/tests/hardlinks_deduplication.rs +++ b/tests/hardlinks_deduplication.rs @@ -3,7 +3,6 @@ pub mod _utils; pub use _utils::*; - use command_extra::CommandExtra; use into_sorted::IntoSorted; use itertools::Itertools; diff --git a/tests/hardlinks_deduplication_multi_args.rs b/tests/hardlinks_deduplication_multi_args.rs index 4ce72cdb..73fbbfb3 100644 --- a/tests/hardlinks_deduplication_multi_args.rs +++ b/tests/hardlinks_deduplication_multi_args.rs @@ -3,7 +3,6 @@ pub mod _utils; pub use _utils::*; - use command_extra::CommandExtra; use into_sorted::IntoSorted; use itertools::Itertools; diff --git a/tests/hardlinks_without_deduplication.rs b/tests/hardlinks_without_deduplication.rs index 9705caae..5f37a23c 100644 --- a/tests/hardlinks_without_deduplication.rs +++ b/tests/hardlinks_without_deduplication.rs @@ -3,7 +3,6 @@ pub mod _utils; pub use _utils::*; - use command_extra::CommandExtra; use parallel_disk_usage::data_tree::Reflection; use parallel_disk_usage::json_data::{JsonData, JsonTree}; diff --git a/tests/json.rs b/tests/json.rs index 93b199d4..7063e5c9 100644 --- a/tests/json.rs +++ b/tests/json.rs @@ -3,7 +3,6 @@ pub mod _utils; pub use _utils::*; - use assert_cmp::assert_op_expr; use command_extra::CommandExtra; use parallel_disk_usage::bytes_format::BytesFormat; diff --git a/tests/one_file_system.rs b/tests/one_file_system.rs index 62d19633..86fa29dc 100644 --- a/tests/one_file_system.rs +++ b/tests/one_file_system.rs @@ -19,7 +19,6 @@ pub mod _utils; pub use _utils::*; - use command_extra::CommandExtra; use parallel_disk_usage::bytes_format::BytesFormat; use parallel_disk_usage::data_tree::DataTree; diff --git a/tests/usual_cli.rs b/tests/usual_cli.rs index 57cd4c79..f75d8bbe 100644 --- a/tests/usual_cli.rs +++ b/tests/usual_cli.rs @@ -1,8 +1,16 @@ #![cfg(feature = "cli")] +#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] +#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::import_grouping, + reason = "single_group cannot keep #[cfg]-gated imports in their own trailing group; see issue #436" + ) +)] pub mod _utils; pub use _utils::*; - use command_extra::CommandExtra; use parallel_disk_usage::bytes_format::BytesFormat; use parallel_disk_usage::data_tree::DataTree;