Skip to content

Commit

Permalink
fix: parsing derive block (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
maksym-arutyunyan authored Sep 2, 2024
1 parent 41f5618 commit e333705
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 45 deletions.
90 changes: 45 additions & 45 deletions src/strategies/rust_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ pub(crate) fn process(lines: Vec<String>, strategy: Strategy) -> io::Result<Vec<
is_sorting_block = true;
block.push(line.clone());
}
if is_sorting_block && RE_DERIVE_END.is_match(&line) {
let line_without_comment = line.trim().split("//").next().unwrap_or("").trim();
if is_sorting_block && RE_DERIVE_END.is_match(line_without_comment) {
if !is_derive_begin {
block.push(line.clone());
}
Expand Down Expand Up @@ -62,56 +63,55 @@ fn sort(block: Vec<String>, is_ignore_block_prev_line: bool, strategy: Strategy)
.map(|line| line.trim_end_matches('\n'))
.collect();
let line = format!("{}\n", line);
let trimmed_line = line.trim();
let line_without_comment = line.trim().split("//").next().unwrap_or("").trim();

let mut result = Vec::new();
// Check if the line contains a #[derive(...)] statement
if let Some(derive_start) = trimmed_line.find("#[derive(") {
if let Some(derive_end) = trimmed_line[derive_start..].find(")]") {
let derive_content = &trimmed_line[derive_start + 9..derive_start + derive_end];
let mut traits: Vec<&str> = derive_content.split(',').map(str::trim).collect();

match strategy {
Strategy::RustDeriveAlphabetical => {
traits.sort_unstable();
}
Strategy::RustDeriveCanonical => {
traits = canonical_sort(traits);
}
_ => {
return block;
}
}
traits.retain(|t| !t.is_empty());
let sorted_traits = traits.join(", ");
let new_derive = format!("#[derive({})]", sorted_traits);

// Reconstruct the line with preserved whitespaces
let prefix_whitespace = &line[..line.find(trimmed_line).unwrap_or(0)];
let suffix_whitespace =
&line[line.rfind(trimmed_line).unwrap_or(line.len()) + trimmed_line.len()..];

let new_line = format!("{}{}{}", prefix_whitespace, new_derive, suffix_whitespace);
if new_line.len() <= STAY_ONE_LINE_LEN {
result.push(new_line);
} else {
let mid_line = format!("{} {},", prefix_whitespace, sorted_traits);
if mid_line.len() <= BREAK_INTO_MANY_LINES_LEN {
result.push(format!("{}#[derive(\n", prefix_whitespace));
result.push(format!("{}\n", mid_line));
result.push(format!("{})]\n", prefix_whitespace));
} else {
result.push(format!("{}#[derive(\n", prefix_whitespace));
for x in traits {
result.push(format!("{} {},\n", prefix_whitespace, x));
}
result.push(format!("{})]\n", prefix_whitespace));
}
if let Some(derive_range) = line_without_comment.find("#[derive(").and_then(|start| {
let end = line_without_comment[start..].find(")]")?;
Some(start + 9..start + end)
}) {
let derive_content = &line_without_comment[derive_range.clone()];
let mut traits: Vec<&str> = derive_content
.split(',')
.map(str::trim)
.filter(|t| !t.is_empty())
.collect();

match strategy {
Strategy::RustDeriveAlphabetical => traits.sort_unstable(),
Strategy::RustDeriveCanonical => traits = canonical_sort(traits),
_ => return block,
}

let sorted_traits = traits.join(", ");
let new_derive = format!("#[derive({})]", sorted_traits);

// Preserve the prefix and suffix whitespace
let prefix_whitespace = &line[..line.find(line_without_comment).unwrap_or(0)];
let suffix_whitespace =
&line[line_without_comment.len() + line.find(line_without_comment).unwrap_or(0)..];

let new_line = format!("{}{}{}", prefix_whitespace, new_derive, suffix_whitespace);
if new_line.len() <= STAY_ONE_LINE_LEN {
return vec![new_line];
}

let mid_line = format!("{} {},", prefix_whitespace, sorted_traits);
let mut result = vec![format!("{}#[derive(\n", prefix_whitespace)];

if mid_line.len() <= BREAK_INTO_MANY_LINES_LEN {
result.push(format!("{}\n{})]\n", mid_line, prefix_whitespace));
} else {
for trait_item in traits {
result.push(format!("{} {},\n", prefix_whitespace, trait_item));
}
result.push(format!("{})]\n", prefix_whitespace));
}

return result;
}

result
block
}

fn canonical_sort(traits: Vec<&str>) -> Vec<&str> {
Expand Down
49 changes: 49 additions & 0 deletions tests/rust_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,52 @@ struct Data {}
"#
);
}

#[test]
fn rust_derive_issue_25_1() {
test_inner!(
RustDeriveAlphabetical,
r#"
#[derive(Parser, Debug)] // Some comment.
struct Data {}
"#,
r#"
#[derive(Debug, Parser)] // Some comment.
struct Data {}
"#
);
}

#[test]
fn rust_derive_issue_25_2() {
test_inner!(
RustDeriveAlphabetical,
r#"
#[derive(Parser, Debug)] // Some comment.
#[command(about = "description", long_about = None)]
struct Data {}
"#,
r#"
#[derive(Debug, Parser)] // Some comment.
#[command(about = "description", long_about = None)]
struct Data {}
"#
);
}

#[test]
fn rust_derive_issue_25_3() {
test_inner!(
RustDeriveAlphabetical,
r#"
#[derive(Parser, Debug)] // Some comment comment with #[derive(Parser, Debug)].
#[command(about = "description", long_about = None)]
struct Data {}
"#,
r#"
#[derive(Debug, Parser)] // Some comment comment with #[derive(Parser, Debug)].
#[command(about = "description", long_about = None)]
struct Data {}
"#
);
}

0 comments on commit e333705

Please sign in to comment.