xsd_parser/code/
ident_path.rs1use std::ops::{Deref, DerefMut};
2use std::str::FromStr;
3
4use proc_macro2::{Ident as Ident2, TokenStream};
5use quote::{format_ident, quote, ToTokens};
6use smallvec::SmallVec;
7
8use crate::{schema::NamespaceId, types::Types};
9
10use super::format_module_ident;
11
12#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct IdentPath {
23 path: Option<ModulePath>,
24 ident: Ident2,
25}
26
27#[derive(Default, Debug, Clone, Eq, PartialEq)]
32pub struct ModulePath(pub SmallVec<[Ident2; 2]>);
33
34impl IdentPath {
35 #[must_use]
38 pub fn from_parts<I>(path: I, ident: Ident2) -> Self
39 where
40 I: IntoIterator<Item = Ident2>,
41 {
42 Self::from_ident(ident).with_path(path)
43 }
44
45 #[must_use]
48 pub fn from_ident(ident: Ident2) -> Self {
49 Self { ident, path: None }
50 }
51
52 #[must_use]
54 pub fn with_ident(mut self, ident: Ident2) -> Self {
55 self.ident = ident;
56
57 self
58 }
59
60 #[must_use]
62 pub fn with_path<I>(mut self, path: I) -> Self
63 where
64 I: IntoIterator<Item = Ident2>,
65 {
66 self.path = Some(ModulePath(path.into_iter().collect()));
67
68 self
69 }
70
71 #[must_use]
74 pub fn into_parts(self) -> (Ident2, Option<ModulePath>) {
75 let Self { ident, path } = self;
76
77 (ident, path)
78 }
79
80 #[must_use]
82 pub fn ident(&self) -> &Ident2 {
83 &self.ident
84 }
85
86 #[must_use]
91 pub fn relative_to(&self, dst: &ModulePath) -> TokenStream {
92 let ident = &self.ident;
93
94 let Some(src) = &self.path else {
95 return quote!(#ident);
96 };
97
98 let mut ret = TokenStream::new();
99 let mut src = src.0.iter().fuse();
100 let mut dst = dst.0.iter().fuse();
101
102 macro_rules! push {
103 ($x:expr) => {{
104 let x = $x;
105 if ret.is_empty() {
106 ret.extend(x)
107 } else {
108 ret.extend(quote!(::#x))
109 }
110 }};
111 }
112
113 loop {
114 match (src.next(), dst.next()) {
115 (Some(a), Some(b)) if a == b => {}
116 (Some(a), Some(_)) => {
117 push!(quote!(super));
118 while dst.next().is_some() {
119 push!(quote!(super));
120 }
121
122 push!(quote!(#a));
123 for a in src {
124 push!(quote!(#a));
125 }
126
127 push!(quote!(#ident));
128
129 return ret;
130 }
131 (Some(a), None) => push!(quote!(#a)),
132 (None, Some(_)) => push!(quote!(super)),
133 (None, None) => {
134 push!(quote!(#ident));
135 return ret;
136 }
137 }
138 }
139 }
140}
141
142impl FromStr for IdentPath {
143 type Err = ();
144
145 fn from_str(s: &str) -> Result<Self, Self::Err> {
146 let mut ident = None;
147 let mut path = ModulePath::default();
148
149 for part in s.split("::") {
150 let part = part.trim();
151 if part.is_empty() {
152 continue;
153 }
154
155 if let Some(ident) = ident.take() {
156 path.0.push(ident);
157 }
158
159 ident = Some(format_ident!("{part}"));
160 }
161
162 Ok(Self {
163 ident: ident.ok_or(())?,
164 path: Some(path),
165 })
166 }
167}
168
169impl ToTokens for IdentPath {
170 fn to_tokens(&self, tokens: &mut TokenStream) {
171 if let Some(path) = &self.path {
172 for module in &path.0 {
173 tokens.extend(quote!(#module::));
174 }
175 }
176
177 let ident = self.ident();
178
179 tokens.extend(quote!(#ident));
180 }
181}
182
183impl ModulePath {
184 #[must_use]
190 pub fn from_namespace(ns: Option<NamespaceId>, types: &Types) -> Self {
191 let ident = ns
192 .and_then(|id| types.modules.get(&id))
193 .and_then(|module| module.name.as_ref())
194 .map(format_module_ident);
195
196 Self(ident.into_iter().collect())
197 }
198
199 #[must_use]
201 pub fn join(mut self, other: Ident2) -> Self {
202 self.0.push(other);
203
204 self
205 }
206}
207
208impl Deref for ModulePath {
209 type Target = SmallVec<[Ident2; 2]>;
210
211 fn deref(&self) -> &Self::Target {
212 &self.0
213 }
214}
215
216impl DerefMut for ModulePath {
217 fn deref_mut(&mut self) -> &mut Self::Target {
218 &mut self.0
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use quote::{format_ident, quote};
225
226 use super::{IdentPath, ModulePath};
227
228 #[test]
229 #[rustfmt::skip]
230 fn type_path() {
231 let string = IdentPath::from_ident(format_ident!("String"));
232 let my_type = IdentPath::from_parts(
233 [format_ident!("my_module")],
234 format_ident!("MyType"),
235 );
236 let serializer = IdentPath::from_parts(
237 [
238 format_ident!("my_module"),
239 format_ident!("quick_xml_serialize"),
240 ],
241 format_ident!("MyTypeSerializer"),
242 );
243 let deserializer = IdentPath::from_parts(
244 [
245 format_ident!("my_module"),
246 format_ident!("quick_xml_deserialize"),
247 ],
248 format_ident!("MyTypeDeserializer"),
249 );
250
251 let empty_path = ModulePath::default();
252 let module_path = ModulePath::default().join(format_ident!("my_module"));
253 let other_module_path = ModulePath::default().join(format_ident!("other_module"));
254 let serializer_path = module_path.clone().join(format_ident!("quick_xml_serialize"));
255 let deserializer_path = module_path.clone().join(format_ident!("quick_xml_deserialize"));
256
257 macro_rules! test {
258 ($actual:expr, $( $expected:tt )*) => {{
259 let a = $actual.to_string();
260 let b = quote!($( $expected )*).to_string();
261
262 assert_eq!(a, b);
263 }};
264 }
265
266 test!(string.relative_to(&empty_path), String);
269 test!(string.relative_to(&module_path), String);
270 test!(string.relative_to(&other_module_path), String);
271 test!(string.relative_to(&serializer_path), String);
272 test!(string.relative_to(&deserializer_path), String);
273
274 test!(my_type.relative_to(&empty_path), my_module::MyType);
275 test!(my_type.relative_to(&module_path), MyType);
276 test!(my_type.relative_to(&other_module_path), super::my_module::MyType);
277 test!(my_type.relative_to(&serializer_path), super::MyType);
278 test!(my_type.relative_to(&deserializer_path), super::MyType);
279
280 test!(serializer.relative_to(&empty_path), my_module::quick_xml_serialize::MyTypeSerializer);
281 test!(serializer.relative_to(&module_path), quick_xml_serialize::MyTypeSerializer);
282 test!(serializer.relative_to(&other_module_path), super::my_module::quick_xml_serialize::MyTypeSerializer);
283 test!(serializer.relative_to(&serializer_path), MyTypeSerializer);
284 test!(serializer.relative_to(&deserializer_path), super::quick_xml_serialize::MyTypeSerializer);
285
286 test!(deserializer.relative_to(&empty_path), my_module::quick_xml_deserialize::MyTypeDeserializer);
287 test!(deserializer.relative_to(&module_path), quick_xml_deserialize::MyTypeDeserializer);
288 test!(deserializer.relative_to(&other_module_path), super::my_module::quick_xml_deserialize::MyTypeDeserializer);
289 test!(deserializer.relative_to(&serializer_path), super::quick_xml_deserialize::MyTypeDeserializer);
290 test!(deserializer.relative_to(&deserializer_path), MyTypeDeserializer);
291 }
292}