From 19a90d195ae39feb96f8884d5ba401c81432a18a Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan <107496615+maksym-arutyunyan@users.noreply.github.com> Date: Mon, 16 Sep 2024 19:45:23 +0200 Subject: [PATCH] feat: improve Rust derive sorting by ignoring namespace prefixes (#30) --- CHANGELOG.md | 4 +++ src/strategies/rust_derive.rs | 57 +++++++++++++++++++++-------------- tests/rust_derive.rs | 8 ++--- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0929394..5fed91e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/strategies/rust_derive.rs b/src/strategies/rust_derive.rs index 3f3f86f..30dc093 100644 --- a/src/strategies/rust_derive.rs +++ b/src/strategies/rust_derive.rs @@ -78,7 +78,7 @@ fn sort(block: Vec, 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, } @@ -114,39 +114,52 @@ fn sort(block: Vec, 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") } diff --git a/tests/rust_derive.rs b/tests/rust_derive.rs index 2d6573a..548e867 100644 --- a/tests/rust_derive.rs +++ b/tests/rust_derive.rs @@ -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 {} "# ); @@ -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 {} "# );