bpaf_derive/
custom_path.rs1use syn::{
2 punctuated::Punctuated,
3 token::{self, PathSep},
4 visit_mut::{self, VisitMut},
5 PathSegment, UseName, UsePath, UseRename, UseTree,
6};
7
8pub(crate) struct CratePathReplacer {
12 query: syn::Path,
14 target: syn::Path,
16}
17
18impl CratePathReplacer {
19 pub(crate) fn new(target: syn::Path, replacement: syn::Path) -> Self {
20 CratePathReplacer {
21 query: target,
22 target: replacement,
23 }
24 }
25
26 fn path_global_match(&self, input: &mut syn::Path) -> bool {
32 self.query.leading_colon.is_some() && input.leading_colon.is_some()
33 }
34
35 fn path_segments_match(&self, input: &mut syn::Path) -> bool {
39 self.query
40 .segments
41 .iter()
42 .zip(input.segments.iter())
43 .all(|(f, o)| f == o)
44 }
45
46 fn replace_path_if_match(&self, input: &mut syn::Path) {
49 if self.path_global_match(input) && self.path_segments_match(input) {
50 input.leading_colon = self.target.leading_colon;
51 input.segments = self
52 .target
53 .segments
54 .clone()
55 .into_iter()
56 .chain(
57 input
58 .segments
59 .iter()
60 .skip(self.query.segments.iter().count())
61 .cloned(),
62 )
63 .collect::<Punctuated<_, _>>();
64 }
65 }
66
67 fn item_use_global_match(&self, input: &syn::ItemUse) -> bool {
68 self.query.leading_colon == input.leading_colon
69 }
70
71 fn item_use_segments_match<'a, Q: Iterator<Item = &'a PathSegment>>(
72 input: &'a UseTree,
73 query_len: usize,
74 mut query_iter: Q,
75 mut matched_parts: Vec<&'a UseTree>,
76 ) -> Option<(Vec<&'a UseTree>, Option<UseTree>)> {
77 if let Some(next_to_match) = query_iter.next() {
78 match input {
79 UseTree::Path(path) => {
80 if next_to_match.ident == path.ident {
81 matched_parts.push(input);
82 return Self::item_use_segments_match(
83 path.tree.as_ref(),
84 query_len,
85 query_iter,
86 matched_parts,
87 );
88 }
89 }
90 UseTree::Name(name) => {
91 if next_to_match.ident == name.ident {
92 if query_iter.next().is_some() {
93 return None;
94 } else {
95 matched_parts.push(input);
96 }
97 }
98 }
99 UseTree::Rename(rename) => {
100 if next_to_match.ident == rename.ident {
101 if query_iter.next().is_some() {
102 return None;
103 } else {
104 matched_parts.push(input);
105 }
106 }
107 }
108 UseTree::Glob(_) => {}
109 UseTree::Group(_) => {}
110 }
111 }
112
113 if query_len == matched_parts.len() {
114 Some((matched_parts, Some(input.clone())))
115 } else {
116 None
117 }
118 }
119
120 fn append_suffix_to_target(
121 &self,
122 matched_parts: Vec<&UseTree>,
123 suffix: Option<UseTree>,
124 ) -> UseTree {
125 let last_input_match = matched_parts
126 .last()
127 .expect("If a match exists, then it the matched prefix must be non-empty.");
128 let mut rev_target_ids = self.target.segments.iter().map(|s| s.ident.clone()).rev();
129 let mut result_tree = match last_input_match {
130 UseTree::Path(_) => {
131 if let Some(suffix_tree) = suffix {
132 UseTree::Path(UsePath {
133 ident: rev_target_ids.next().expect(
134 "error while making a `UseTree::Path`: target should not be empty",
135 ),
136 colon2_token: PathSep::default(),
137 tree: Box::new(suffix_tree),
138 })
139 } else {
140 unreachable!("If the last part of the matched input was a path, then there must be some suffix left to attach to complete it.")
141 }
142 }
143 UseTree::Name(_) => {
144 assert!(suffix.is_none(), "If the last part of the matched input was a syn::UseTree::Name, then there shouldn't be any suffix left to attach to the prefix.");
145 UseTree::Name(UseName {
146 ident: rev_target_ids
147 .next()
148 .expect("error while making a `UseTree::Name`: target should not be empty"),
149 })
150 }
151 UseTree::Rename(original_rename) => {
152 assert!(suffix.is_none(), "If the last part of the matched input was a syn::UseTree::Rename, then there shouldn't be any suffix left to attach to the prefix.");
153 UseTree::Rename(UseRename {
154 ident: rev_target_ids.next().expect(
155 "error while making a `UseTree::Rename`: target should not be empty",
156 ),
157 as_token: token::As::default(),
158 rename: original_rename.rename.clone(),
159 })
160 }
161 UseTree::Glob(_) => unreachable!(
162 "There is no functionality for matching against a syn::UseTree::Group."
163 ),
164 UseTree::Group(_) => unreachable!(
165 "There is no functionality for matching against a syn::UseTree::Group."
166 ),
167 };
168 for id in rev_target_ids {
169 result_tree = UseTree::Path(UsePath {
170 ident: id,
171 colon2_token: PathSep::default(),
172 tree: Box::new(result_tree),
173 })
174 }
175 result_tree
176 }
177
178 fn replace_item_use_if_match(&self, input: &mut syn::ItemUse) {
181 if self.item_use_global_match(input) {
182 if let Some((matched_prefix, suffix)) = Self::item_use_segments_match(
183 &input.tree,
184 self.query.segments.len(),
185 self.query.segments.iter(),
186 vec![],
187 ) {
188 input.leading_colon = self.target.leading_colon;
189 input.tree = self.append_suffix_to_target(matched_prefix, suffix);
190 }
191 }
192 }
193}
194
195impl VisitMut for CratePathReplacer {
196 fn visit_path_mut(&mut self, path: &mut syn::Path) {
197 self.replace_path_if_match(path);
198 visit_mut::visit_path_mut(self, path);
199 }
200
201 fn visit_item_use_mut(&mut self, item_use: &mut syn::ItemUse) {
202 self.replace_item_use_if_match(item_use);
203 visit_mut::visit_item_use_mut(self, item_use);
204 }
205}