• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use quote::quote;
2 use std::cmp::Ordering;
3 use syn::{Arm, Attribute, Ident, Result, Variant};
4 use syn::{Error, Field, Pat, PatIdent};
5 
6 use crate::compare::{cmp, Path, UnderscoreOrder};
7 use crate::format;
8 use crate::parse::Input::{self, *};
9 
sorted(input: &mut Input) -> Result<()>10 pub fn sorted(input: &mut Input) -> Result<()> {
11     let paths = match input {
12         Enum(item) => collect_paths(&mut item.variants)?,
13         Struct(item) => collect_paths(&mut item.fields)?,
14         Match(expr) | Let(expr) => collect_paths(&mut expr.arms)?,
15     };
16 
17     let mode = UnderscoreOrder::First;
18     if find_misordered(&paths, mode).is_none() {
19         return Ok(());
20     }
21 
22     let mode = UnderscoreOrder::Last;
23     let wrong = match find_misordered(&paths, mode) {
24         Some(wrong) => wrong,
25         None => return Ok(()),
26     };
27 
28     let lesser = &paths[wrong];
29     let correct_pos = match paths[..wrong - 1].binary_search_by(|probe| cmp(probe, lesser, mode)) {
30         Err(correct_pos) => correct_pos,
31         Ok(equal_to) => equal_to + 1,
32     };
33     let greater = &paths[correct_pos];
34     Err(format::error(lesser, greater))
35 }
36 
find_misordered(paths: &[Path], mode: UnderscoreOrder) -> Option<usize>37 fn find_misordered(paths: &[Path], mode: UnderscoreOrder) -> Option<usize> {
38     for i in 1..paths.len() {
39         if cmp(&paths[i], &paths[i - 1], mode) == Ordering::Less {
40             return Some(i);
41         }
42     }
43 
44     None
45 }
46 
collect_paths<'a, I, P>(iter: I) -> Result<Vec<Path>> where I: IntoIterator<Item = &'a mut P>, P: Sortable + 'a,47 fn collect_paths<'a, I, P>(iter: I) -> Result<Vec<Path>>
48 where
49     I: IntoIterator<Item = &'a mut P>,
50     P: Sortable + 'a,
51 {
52     iter.into_iter()
53         .filter_map(|item| {
54             if remove_unsorted_attr(item.attrs()) {
55                 None
56             } else {
57                 Some(item.to_path())
58             }
59         })
60         .collect()
61 }
62 
remove_unsorted_attr(attrs: &mut Vec<Attribute>) -> bool63 fn remove_unsorted_attr(attrs: &mut Vec<Attribute>) -> bool {
64     for i in 0..attrs.len() {
65         let path = &attrs[i].path;
66         let path = quote!(#path).to_string();
67         if path == "unsorted" || path == "remain :: unsorted" {
68             attrs.remove(i);
69             return true;
70         }
71     }
72 
73     false
74 }
75 
76 trait Sortable {
to_path(&self) -> Result<Path>77     fn to_path(&self) -> Result<Path>;
attrs(&mut self) -> &mut Vec<Attribute>78     fn attrs(&mut self) -> &mut Vec<Attribute>;
79 }
80 
81 impl Sortable for Variant {
to_path(&self) -> Result<Path>82     fn to_path(&self) -> Result<Path> {
83         Ok(Path {
84             segments: vec![self.ident.clone()],
85         })
86     }
attrs(&mut self) -> &mut Vec<Attribute>87     fn attrs(&mut self) -> &mut Vec<Attribute> {
88         &mut self.attrs
89     }
90 }
91 
92 impl Sortable for Field {
to_path(&self) -> Result<Path>93     fn to_path(&self) -> Result<Path> {
94         Ok(Path {
95             segments: vec![self.ident.clone().expect("must be named field")],
96         })
97     }
attrs(&mut self) -> &mut Vec<Attribute>98     fn attrs(&mut self) -> &mut Vec<Attribute> {
99         &mut self.attrs
100     }
101 }
102 
103 impl Sortable for Arm {
to_path(&self) -> Result<Path>104     fn to_path(&self) -> Result<Path> {
105         // Sort by just the first pat.
106         let pat = match &self.pat {
107             Pat::Or(pat) => pat.cases.iter().next().expect("at least one pat"),
108             _ => &self.pat,
109         };
110 
111         let segments = match pat {
112             Pat::Ident(pat) if is_just_ident(pat) => vec![pat.ident.clone()],
113             Pat::Path(pat) => idents_of_path(&pat.path),
114             Pat::Struct(pat) => idents_of_path(&pat.path),
115             Pat::TupleStruct(pat) => idents_of_path(&pat.path),
116             Pat::Wild(pat) => vec![Ident::from(pat.underscore_token)],
117             other => {
118                 let msg = "unsupported by #[remain::sorted]";
119                 return Err(Error::new_spanned(other, msg));
120             }
121         };
122 
123         Ok(Path { segments })
124     }
attrs(&mut self) -> &mut Vec<Attribute>125     fn attrs(&mut self) -> &mut Vec<Attribute> {
126         &mut self.attrs
127     }
128 }
129 
idents_of_path(path: &syn::Path) -> Vec<Ident>130 fn idents_of_path(path: &syn::Path) -> Vec<Ident> {
131     path.segments.iter().map(|seg| seg.ident.clone()).collect()
132 }
133 
is_just_ident(pat: &PatIdent) -> bool134 fn is_just_ident(pat: &PatIdent) -> bool {
135     pat.by_ref.is_none() && pat.mutability.is_none() && pat.subpat.is_none()
136 }
137