1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
use syn::{Arm, Ident, Result, Variant};
use syn::{Error, Field, Pat, PatIdent};
use crate::compare::Path;
use crate::format;
use crate::parse::Input::{self, *};
pub fn sorted(input: Input) -> Result<()> {
let paths = match input {
Enum(item) => collect_paths(item.variants)?,
Struct(fields) => collect_paths(fields.named)?,
Match(expr) | Let(expr) => collect_paths(expr.arms)?,
};
for i in 1..paths.len() {
let cur = &paths[i];
if *cur < paths[i - 1] {
let lesser = cur;
let correct_pos = paths[..i - 1].binary_search(cur).unwrap_err();
let greater = &paths[correct_pos];
return Err(format::error(lesser, greater));
}
}
Ok(())
}
fn collect_paths<I>(iter: I) -> Result<Vec<Path>>
where
I: IntoIterator,
I::Item: IntoPath,
{
iter.into_iter().map(IntoPath::into_path).collect()
}
trait IntoPath {
fn into_path(self) -> Result<Path>;
}
impl IntoPath for Variant {
fn into_path(self) -> Result<Path> {
Ok(Path {
segments: vec![self.ident],
})
}
}
impl IntoPath for Field {
fn into_path(self) -> Result<Path> {
Ok(Path {
segments: vec![self.ident.expect("must be named field")],
})
}
}
impl IntoPath for Arm {
fn into_path(self) -> Result<Path> {
// Sort by just the first pat.
let pat = self.pats.into_iter().next().expect("at least one pat");
let segments = match pat {
Pat::Wild(pat) => vec![Ident::from(pat.underscore_token)],
Pat::Path(pat) => idents_of_path(pat.path),
Pat::Struct(pat) => idents_of_path(pat.path),
Pat::TupleStruct(pat) => idents_of_path(pat.path),
Pat::Ident(ref pat) if is_just_ident(pat) => vec![pat.ident.clone()],
other => {
let msg = "unsupported by #[remain::sorted]";
return Err(Error::new_spanned(other, msg));
}
};
Ok(Path { segments })
}
}
fn idents_of_path(path: syn::Path) -> Vec<Ident> {
path.segments.into_iter().map(|seg| seg.ident).collect()
}
fn is_just_ident(pat: &PatIdent) -> bool {
pat.by_ref.is_none() && pat.mutability.is_none() && pat.subpat.is_none()
}
|