Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion dylint.toml
Original file line number Diff line number Diff line change
@@ -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"]
Expand All @@ -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"]
Expand Down
11 changes: 9 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)]
Expand Down
8 changes: 8 additions & 0 deletions src/app/hdd.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/app/hdd/test_linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
9 changes: 4 additions & 5 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
9 changes: 8 additions & 1 deletion src/bytes_format.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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")]
Expand Down
12 changes: 5 additions & 7 deletions src/data_tree.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
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
/// of a filesystem.
/// * 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)]
Expand Down
24 changes: 16 additions & 8 deletions src/data_tree/reflection.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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"))]
Expand Down
8 changes: 8 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand Down
8 changes: 8 additions & 0 deletions src/get_size.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
22 changes: 14 additions & 8 deletions src/hardlink/hardlink_list.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -45,7 +51,7 @@ struct Value<Size> {

/// 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)]
Expand Down
14 changes: 11 additions & 3 deletions src/hardlink/hardlink_list/reflection.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Size>(Vec<ReflectionEntry<Size>>);
Expand Down
8 changes: 8 additions & 0 deletions src/hardlink/hardlink_list/summary.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
6 changes: 2 additions & 4 deletions src/hardlink/link_path_list.rs
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
14 changes: 11 additions & 3 deletions src/hardlink/link_path_list/reflection.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<PathBuf>);
Expand Down
8 changes: 8 additions & 0 deletions src/inode.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand Down
Loading
Loading