Skip to content

Commit

Permalink
feat: improve Rust derive sorting by ignoring namespace prefixes (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
maksym-arutyunyan authored Sep 16, 2024
1 parent 2499b83 commit 19a90d1
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ format and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [Unreleased]

### Added

- Keep Rust derive tokens together, eg. `Serialize` and `serde::Serialize`

## [0.1.0] - 2024-09-12

### Added
Expand Down
57 changes: 35 additions & 22 deletions src/strategies/rust_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn sort(block: Vec<String>, is_ignore_block_prev_line: bool, strategy: Strategy)
.collect();

match strategy {
Strategy::RustDeriveAlphabetical => traits.sort_unstable(),
Strategy::RustDeriveAlphabetical => traits = aphabetical_sort(traits),
Strategy::RustDeriveCanonical => traits = canonical_sort(traits),
_ => return block,
}
Expand Down Expand Up @@ -114,39 +114,52 @@ fn sort(block: Vec<String>, is_ignore_block_prev_line: bool, strategy: Strategy)
block
}

fn canonical_sort(traits: Vec<&str>) -> Vec<&str> {
// Define the canonical order of traits
let canonical_order = [
"Copy",
"Clone",
"Eq",
"PartialEq",
"Ord",
"PartialOrd",
"Hash",
"Debug",
"Display",
"Default",
];

// Create a mapping from trait to its canonical index
let canonical_index: std::collections::HashMap<_, _> = canonical_order
fn extract_last_token(s: &str) -> &str {
s.split("::").last().unwrap_or(s)
}

fn priority_sort<'a>(traits: Vec<&'a str>, priority_traits: &[&'a str]) -> Vec<&'a str> {
// Create a mapping from trait to its priority index
let priority_index: std::collections::HashMap<_, _> = priority_traits
.iter()
.enumerate()
.map(|(i, &trait_name)| (trait_name, i))
.collect();

// Sort traits by canonical index, and by trait name if indices are the same
// Sort traits by priority index, and by trait name if indices are the same
let mut sorted_traits = traits;
sorted_traits.sort_by(|a, b| {
let index_a = canonical_index.get(a).unwrap_or(&usize::MAX);
let index_b = canonical_index.get(b).unwrap_or(&usize::MAX);
(index_a, a).cmp(&(index_b, b))
let index_a = priority_index.get(a).unwrap_or(&usize::MAX);
let index_b = priority_index.get(b).unwrap_or(&usize::MAX);
(index_a, extract_last_token(a), a).cmp(&(index_b, extract_last_token(b), b))
});

sorted_traits
}

fn aphabetical_sort(traits: Vec<&str>) -> Vec<&str> {
priority_sort(traits, &[])
}

fn canonical_sort(traits: Vec<&str>) -> Vec<&str> {
priority_sort(
traits,
// Define the canonical order of traits
&[
"Copy",
"Clone",
"Eq",
"PartialEq",
"Ord",
"PartialOrd",
"Hash",
"Debug",
"Display",
"Default",
],
)
}

fn re_derive_begin() -> Regex {
Regex::new(r"^\s*#\[derive\(").expect("Failed to build regex for rust derive begin")
}
Expand Down
8 changes: 4 additions & 4 deletions tests/rust_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ fn rust_derive_alphabetical() {
test_inner!(
RustDeriveAlphabetical,
r#"
#[derive(C, B, A, Ord, Copy)]
#[derive(serde::Serialize, C, B, A, Ord, Copy, c, b, a, Serialize)]
struct Data {}
"#,
r#"
#[derive(A, B, C, Copy, Ord)]
#[derive(A, B, C, Copy, Ord, Serialize, serde::Serialize, a, b, c)]
struct Data {}
"#
);
Expand All @@ -23,11 +23,11 @@ fn rust_derive_canonical() {
test_inner!(
RustDeriveCanonical,
r#"
#[derive(C, B, A, Ord, Copy)]
#[derive(serde::Serialize, C, B, A, Ord, Copy, c, b, a, Serialize)]
struct Data {}
"#,
r#"
#[derive(Copy, Ord, A, B, C)]
#[derive(Copy, Ord, A, B, C, Serialize, serde::Serialize, a, b, c)]
struct Data {}
"#
);
Expand Down

0 comments on commit 19a90d1

Please sign in to comment.