Skip to content

Commit c002b16

Browse files
authored
Support sorting (#8)
1 parent 6dc984d commit c002b16

9 files changed

Lines changed: 270 additions & 43 deletions

File tree

mock/Cargo.toml.expect

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ std = [
4848
"pallet-b/std",
4949
"pallet-c/std",
5050
# "pallet-d/std",
51-
"primitive-a/std",
5251
"pallet-a/std",
5352
"pallet-d/std",
53+
"primitive-a/std",
5454
]
5555

5656
runtime-benchmarks = [
5757
"pallet-a/runtime-benchmarks",
5858
# "pallet-b/runtime-benchmarks",
59-
"pallet-c/runtime-benchmarks",
6059
"pallet-b/runtime-benchmarks",
60+
"pallet-c/runtime-benchmarks",
6161
"pallet-d/runtime-benchmarks",
6262
# "pallet-d/runtime-benchmarks",
6363
]

mock/nested/a/Cargo.toml.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ nested-d = { path = "../d", default-features = true }
1616
[features]
1717
default = ["std"]
1818
std = [
19-
"nested-d/std",
2019
"nested-b/std",
20+
"nested-d/std",
2121
]

src/analyzer.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use fxhash::FxHashMap;
1111
use once_cell::sync::{Lazy, OnceCell};
1212
use serde::Serialize;
1313
// cargo-featalign
14-
use crate::{cli::AnalyzerInitiator, prelude::*, util::GetById};
14+
use crate::{cli::AnalyzerInitiator, prelude::*, shared::FEATURES, util::GetById};
1515

1616
#[allow(clippy::type_complexity)]
1717
pub static PROBLEMS: Lazy<Arc<Mutex<FxHashMap<PackageId, Vec<ProblemCrate>>>>> =
@@ -37,7 +37,6 @@ static DEFAULT_STD: OnceCell<bool> = OnceCell::new();
3737

3838
#[derive(Debug, Clone)]
3939
pub struct Analyzer {
40-
features: Arc<Vec<String>>,
4140
// TODO?: replace with `FxHashMap` packages
4241
metadata: Arc<Metadata>,
4342
// Remove?
@@ -62,11 +61,7 @@ impl Analyzer {
6261
});
6362
let resolve = mem::take(&mut metadata.resolve).unwrap();
6463

65-
Self {
66-
features: Arc::new(initiator.features),
67-
metadata: Arc::new(metadata),
68-
resolve: Arc::new(resolve),
69-
}
64+
Self { metadata: Arc::new(metadata), resolve: Arc::new(resolve) }
7065
}
7166

7267
pub fn analyze(self, depth: i16) {
@@ -166,11 +161,10 @@ impl Analyzer {
166161
let p_name = p.name.as_str();
167162
let p_alias = rs.get_by_id(p_name).unwrap_or(p_name);
168163
let n = self.resolve.get_by_id(p_id).unwrap();
164+
let fs = FEATURES.get().unwrap();
169165
let mut missing_fs = Vec::new();
170166

171-
for (f, required_fs) in
172-
package.features.iter().filter(|(f, _)| self.features.contains(f))
173-
{
167+
for (f, required_fs) in package.features.iter().filter(|(f, _)| fs.contains(f)) {
174168
// If the dependency has the feature specified by the user for analyzing.
175169
if n.features.contains(f) {
176170
let mut problematic = true;

src/cli.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ pub struct Cli {
4242

4343
#[derive(Debug, Parser)]
4444
pub struct SharedInitiator {
45+
/// Features to process.
46+
#[arg(long, required = true, value_name = "[NAME]", value_delimiter = ',')]
47+
pub features: Vec<String>,
4548
/// Number of threads to use.
4649
///
4750
/// The default value is based on the number of logical cores.
@@ -55,6 +58,12 @@ pub struct SharedInitiator {
5558
/// Overwrite: Overwrites the original `Cargo.toml` file.
5659
#[arg(long, value_enum, verbatim_doc_comment, default_value_t = Mode::Overwrite)]
5760
pub mode: Mode,
61+
/// Use the given symbol for indentation.
62+
#[arg(long, value_enum, default_value_t = IndentSymbol::Tab)]
63+
pub indent_symbol: IndentSymbol,
64+
/// The number of spaces used for indentation.
65+
#[arg(long, value_name = "SIZE", default_value_t = 4)]
66+
pub indent_size: usize,
5867
}
5968

6069
#[derive(Debug, Parser)]
@@ -64,9 +73,6 @@ pub struct AnalyzerInitiator {
6473
/// If `Cargo.toml` is not provided, it will be searched for under the specified path.
6574
#[arg(value_name = "PATH", default_value = "./Cargo.toml")]
6675
pub manifest_path: PathBuf,
67-
/// Features to process.
68-
#[arg(long, required = true, value_name = "[NAME]", value_delimiter = ',')]
69-
pub features: Vec<String>,
7076
/// Determines whether to process only workspace members.
7177
#[arg(long)]
7278
pub workspace_only: bool,
@@ -81,12 +87,9 @@ pub struct AnalyzerInitiator {
8187

8288
#[derive(Debug, Parser)]
8389
pub struct ResolverInitiator {
84-
/// Use the given symbol for indentation.
85-
#[arg(long, value_enum, default_value_t = IndentSymbol::Tab)]
86-
pub indent_symbol: IndentSymbol,
87-
/// The number of spaces used for indentation.
88-
#[arg(long, value_name = "SIZE", default_value_t = 4)]
89-
pub indent_size: usize,
90+
/// Wether to sort the required features during alignment.
91+
#[arg(long)]
92+
pub sort: bool,
9093
}
9194
#[derive(Clone, Debug, ValueEnum)]
9295
pub enum IndentSymbol {

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use cli::{Cli, Mode, Parser};
1010
mod analyzer;
1111
use analyzer::{Analyzer, PROBLEMS};
1212

13+
mod sorter;
14+
1315
mod resolver;
1416
use resolver::Resolver;
1517

src/resolver.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,26 @@ use cargo_metadata::PackageId;
99
use fxhash::FxHashMap;
1010
use once_cell::sync::{Lazy, OnceCell};
1111
use regex::Regex;
12-
use toml_edit::{Document, Value};
12+
use toml_edit::{visit_mut::VisitMut, Document, Value};
1313
// cargo-featalign
1414
use crate::{
1515
analyzer::{Problem, ProblemCrate, PROBLEMS},
16-
cli::{IndentSymbol, Mode, ResolverInitiator},
16+
cli::{Mode, ResolverInitiator},
1717
prelude::*,
18-
shared::MODE,
18+
shared::{FEATURES, INDENTATION, MODE},
19+
sorter::SortVisitor,
1920
};
2021

2122
static PATH_REGEX: Lazy<Regex> =
2223
Lazy::new(|| Regex::new(r".+? \d.\d.\d \(path\+file://(/.+?)\)").unwrap());
23-
static INDENTATION: OnceCell<String> = OnceCell::new();
24+
25+
static SORT: OnceCell<bool> = OnceCell::new();
2426

2527
#[derive(Clone, Debug)]
2628
pub struct Resolver;
2729
impl Resolver {
2830
pub fn initialize(initiator: ResolverInitiator) -> Self {
29-
let indentation = match initiator.indent_symbol {
30-
IndentSymbol::Tab => "\n\t".into(),
31-
IndentSymbol::Whitespace => format!("\n{}", " ".repeat(initiator.indent_size)),
32-
};
33-
34-
INDENTATION.set(indentation).unwrap();
31+
SORT.set(initiator.sort).unwrap();
3532

3633
Self
3734
}
@@ -94,12 +91,14 @@ impl Resolver {
9491
}
9592
}
9693

94+
if *SORT.get().unwrap() {
95+
SortVisitor(FEATURES.get().unwrap().to_owned()).visit_document_mut(&mut d);
96+
}
97+
9798
match MODE.get().unwrap() {
9899
Mode::Check => (),
99-
Mode::DryRun => {
100-
println!("{id}\n{}", util::diff(&s, &d.to_string()));
101-
},
102-
m @ Mode::DryRun2 | m @ Mode::Overwrite => {
100+
Mode::DryRun => println!("{id}\n{}", util::diff(&s, &d.to_string())),
101+
m => {
103102
let p_tmp = tmp_path_of(&p);
104103
let f_tmp = File::create(&p_tmp)?;
105104
let mut w = BufWriter::new(f_tmp);

src/shared.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ use std::{
66
// crates.io
77
use once_cell::sync::{Lazy, OnceCell};
88
// cargo-featalign
9-
use crate::cli::{Mode, SharedInitiator};
9+
use crate::cli::{IndentSymbol, Mode, SharedInitiator};
1010

11+
pub static FEATURES: OnceCell<Vec<String>> = OnceCell::new();
1112
pub static MODE: OnceCell<Mode> = OnceCell::new();
13+
pub static INDENTATION: OnceCell<String> = OnceCell::new();
1214

1315
static THREAD: OnceCell<u16> = OnceCell::new();
1416
static THREAD_ACTIVE: Lazy<AtomicU16> = Lazy::new(|| AtomicU16::new(1));
@@ -38,9 +40,17 @@ pub fn deactivate_threads<T>(threads: Vec<JoinHandle<T>>) -> Vec<T> {
3840
pub struct Shared;
3941
impl Shared {
4042
pub fn initialize(initiator: SharedInitiator) -> Self {
43+
FEATURES.set(initiator.features).unwrap();
4144
THREAD.set(initiator.thread).unwrap();
4245
MODE.set(initiator.mode).unwrap();
4346

47+
let indentation = match initiator.indent_symbol {
48+
IndentSymbol::Tab => "\n\t".into(),
49+
IndentSymbol::Whitespace => format!("\n{}", " ".repeat(initiator.indent_size)),
50+
};
51+
52+
INDENTATION.set(indentation).unwrap();
53+
4454
Self
4555
}
4656
}

src/sorter.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// std
2+
use std::mem;
3+
// crates.io
4+
use toml_edit::{visit_mut::VisitMut, Formatted, RawString, Table, Value};
5+
// cargo-featalign
6+
use crate::shared::INDENTATION;
7+
8+
#[derive(Debug)]
9+
pub struct SortVisitor(pub Vec<String>);
10+
impl VisitMut for SortVisitor {
11+
fn visit_table_mut(&mut self, node: &mut Table) {
12+
fn sort(mut v: Vec<Formatted<String>>) -> Vec<Formatted<String>> {
13+
fn has_empty_prefix(formatted_string: &Formatted<String>) -> bool {
14+
formatted_string
15+
.decor()
16+
.prefix()
17+
.and_then(|s| s.as_str())
18+
.map(|s| s.trim().is_empty())
19+
.unwrap_or_default()
20+
}
21+
22+
fn sort_sub(v: &mut Vec<Formatted<String>>, prefix: Option<RawString>) {
23+
v.sort_by(|a, b| a.value().cmp(b.value()));
24+
25+
if let Some(p) = prefix {
26+
v[0].decor_mut().set_prefix(p);
27+
}
28+
}
29+
30+
let mut offset = 0;
31+
let mut pivots = Vec::new();
32+
33+
v.iter_mut().enumerate().for_each(|(i, s)| {
34+
if !has_empty_prefix(s) {
35+
let d = s.decor_mut();
36+
let p = d.prefix().unwrap().to_owned();
37+
38+
pivots.push((i - offset, p));
39+
40+
offset = i;
41+
}
42+
43+
s.decor_mut().clear();
44+
});
45+
46+
let mut prefix = None;
47+
let mut v_chunks = Vec::new();
48+
49+
for (i, p) in pivots {
50+
let v_chunk = v.split_off(i);
51+
52+
if !v.is_empty() {
53+
sort_sub(&mut v, prefix.take());
54+
55+
v_chunks.push(mem::take(&mut v));
56+
}
57+
58+
prefix = Some(p);
59+
v = v_chunk;
60+
}
61+
62+
sort_sub(&mut v, prefix.take());
63+
64+
v_chunks.push(v);
65+
66+
v_chunks.concat()
67+
}
68+
69+
if let Some(v) = node.get_mut("features") {
70+
let t = v.as_table_mut().unwrap();
71+
let pfs = mem::take(&mut self.0);
72+
73+
pfs.into_iter().for_each(|f| {
74+
if let Some(rfs) = t.get_mut(&f) {
75+
let rfs = rfs.as_array_mut().unwrap();
76+
let rfs_ = mem::take(rfs);
77+
78+
rfs.set_trailing(rfs_.trailing().to_owned());
79+
80+
let rfs_values = rfs_
81+
.into_iter()
82+
.map(|v| if let Value::String(s) = v { s } else { unreachable!() })
83+
.collect::<Vec<_>>();
84+
85+
sort(rfs_values).into_iter().for_each(|f| {
86+
let v = if f.decor().prefix().is_none() {
87+
Value::String(f).decorated(INDENTATION.get().unwrap(), "")
88+
} else {
89+
Value::String(f)
90+
};
91+
92+
rfs.push_formatted(v);
93+
});
94+
95+
if !rfs.is_empty() {
96+
rfs.set_trailing_comma(true);
97+
98+
if rfs.trailing().as_str().map(|s| s.is_empty()).unwrap_or(true) {
99+
rfs.set_trailing("\n");
100+
}
101+
}
102+
}
103+
});
104+
}
105+
}
106+
}

0 commit comments

Comments
 (0)