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