1use std::fmt::{Display, Formatter, Result as FmtResult};
2use std::ops::{Deref, DerefMut};
3use std::str::FromStr;
4
5use proc_macro2::{Ident as Ident2, TokenStream};
6use quote::{format_ident, quote, ToTokens};
7use smallvec::SmallVec;
8use thiserror::Error;
9
10use crate::models::{
11 meta::MetaTypes,
12 schema::{NamespaceId, SchemaId},
13 Ident,
14};
15
16#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
26pub struct IdentPath {
27 path: Option<ModulePath>,
28 ident: Ident2,
29 is_absolute: bool,
30}
31
32#[derive(Default, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
37pub struct ModulePath(pub SmallVec<[Ident2; 2]>);
38
39#[derive(Debug, Error)]
41#[error("Invalid identifier path: {0}")]
42pub struct InvalidIdentPath(pub String);
43
44#[derive(Debug, Clone, Copy, Eq, PartialEq)]
46pub enum ModuleIdent {
47 Root,
49
50 Namespace(NamespaceId),
52
53 Schema(SchemaId),
55
56 Both(NamespaceId, SchemaId),
58}
59
60impl IdentPath {
61 #[must_use]
64 pub fn from_parts<I>(path: I, ident: Ident2) -> Self
65 where
66 I: IntoIterator<Item = Ident2>,
67 {
68 Self::from_ident(ident).with_path(path)
69 }
70
71 #[must_use]
74 pub fn from_ident(ident: Ident2) -> Self {
75 Self {
76 ident,
77 path: None,
78 is_absolute: false,
79 }
80 }
81
82 #[must_use]
84 pub fn with_ident(mut self, ident: Ident2) -> Self {
85 self.ident = ident;
86
87 self
88 }
89
90 #[must_use]
92 pub fn with_path<I>(mut self, path: I) -> Self
93 where
94 I: IntoIterator<Item = Ident2>,
95 {
96 self.path = Some(ModulePath(path.into_iter().collect()));
97
98 self
99 }
100
101 #[must_use]
104 pub fn into_parts(self) -> (Ident2, Option<ModulePath>, bool) {
105 let Self {
106 ident,
107 path,
108 is_absolute,
109 } = self;
110
111 (ident, path, is_absolute)
112 }
113
114 #[must_use]
116 pub fn ident(&self) -> &Ident2 {
117 &self.ident
118 }
119
120 #[must_use]
122 pub fn module(&self) -> Option<&ModulePath> {
123 self.path.as_ref()
124 }
125
126 #[must_use]
128 pub fn is_absolute(&self) -> bool {
129 self.is_absolute
130 }
131
132 #[must_use]
137 pub fn relative_to(&self, dst: &ModulePath) -> TokenStream {
138 let ident = &self.ident;
139
140 let Some(src) = &self.path else {
141 return quote!(#ident);
142 };
143
144 let mut ret = TokenStream::new();
145 if self.is_absolute {
146 for p in src.0.iter() {
147 ret.extend(quote!(::#p));
148 }
149
150 return quote!(#ret::#ident);
151 }
152
153 let mut src = src.0.iter().fuse();
154 let mut dst = dst.0.iter().fuse();
155
156 macro_rules! push {
157 ($x:expr) => {{
158 let x = $x;
159 if ret.is_empty() {
160 ret.extend(x)
161 } else {
162 ret.extend(quote!(::#x))
163 }
164 }};
165 }
166
167 loop {
168 match (src.next(), dst.next()) {
169 (Some(a), Some(b)) if a == b => {}
170 (Some(a), Some(_)) => {
171 push!(quote!(super));
172 while dst.next().is_some() {
173 push!(quote!(super));
174 }
175
176 push!(quote!(#a));
177 for a in src {
178 push!(quote!(#a));
179 }
180
181 push!(quote!(#ident));
182
183 return ret;
184 }
185 (Some(a), None) => push!(quote!(#a)),
186 (None, Some(_)) => push!(quote!(super)),
187 (None, None) => {
188 push!(quote!(#ident));
189 return ret;
190 }
191 }
192 }
193 }
194}
195
196impl TryFrom<&str> for IdentPath {
197 type Error = InvalidIdentPath;
198
199 fn try_from(value: &str) -> Result<Self, Self::Error> {
200 Self::from_str(value)
201 }
202}
203
204impl TryFrom<String> for IdentPath {
205 type Error = InvalidIdentPath;
206
207 fn try_from(value: String) -> Result<Self, Self::Error> {
208 Self::from_str(&value)
209 }
210}
211
212impl FromStr for IdentPath {
213 type Err = InvalidIdentPath;
214
215 fn from_str(s: &str) -> Result<Self, Self::Err> {
216 let mut ident = None;
217 let mut path = ModulePath::default();
218 let mut is_absolute = false;
219
220 for part in s.split("::") {
221 let part = part.trim();
222 if part.is_empty() {
223 if path.is_empty() && ident.is_none() {
224 is_absolute = true;
225 }
226
227 continue;
228 }
229
230 if let Some(ident) = ident.take() {
231 path.0.push(ident);
232 }
233
234 ident = Some(format_ident!("{part}"));
235 }
236
237 Ok(Self {
238 ident: ident.ok_or_else(|| InvalidIdentPath(s.into()))?,
239 path: Some(path),
240 is_absolute,
241 })
242 }
243}
244
245impl Display for IdentPath {
246 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
247 if let Some(path) = &self.path {
248 for module in &path.0 {
249 write!(f, "{module}::")?;
250 }
251 }
252
253 write!(f, "{}", self.ident)
254 }
255}
256
257impl ToTokens for IdentPath {
258 fn to_tokens(&self, tokens: &mut TokenStream) {
259 if self.is_absolute {
260 tokens.extend(quote!(::));
261 }
262
263 if let Some(path) = &self.path {
264 for module in &path.0 {
265 tokens.extend(quote!(#module::));
266 }
267 }
268
269 let ident = self.ident();
270
271 tokens.extend(quote!(#ident));
272 }
273}
274
275impl ModulePath {
276 #[must_use]
282 pub fn from_ident(types: &MetaTypes, ident: ModuleIdent) -> Self {
283 let (namespace, schema) = match ident {
284 ModuleIdent::Root => (None, None),
285 ModuleIdent::Namespace(n) => (Some(n), None),
286 ModuleIdent::Schema(s) => (None, Some(s)),
287 ModuleIdent::Both(n, s) => (Some(n), Some(s)),
288 };
289
290 let namespace = namespace
291 .and_then(|id| types.modules.get(&id))
292 .and_then(|module| module.name.as_ref())
293 .map(|name| types.naming.format_module_ident(name));
294 let schema = schema
295 .and_then(|id| types.schemas.get(&id))
296 .and_then(|schema| schema.name.as_ref())
297 .map(|name| types.naming.format_module_ident(name));
298
299 Self(namespace.into_iter().chain(schema).collect())
300 }
301
302 #[must_use]
304 pub fn join(mut self, other: Ident2) -> Self {
305 self.0.push(other);
306
307 self
308 }
309}
310
311impl Deref for ModulePath {
312 type Target = SmallVec<[Ident2; 2]>;
313
314 fn deref(&self) -> &Self::Target {
315 &self.0
316 }
317}
318
319impl DerefMut for ModulePath {
320 fn deref_mut(&mut self) -> &mut Self::Target {
321 &mut self.0
322 }
323}
324
325impl ModuleIdent {
328 pub(crate) fn new(
329 types: &MetaTypes,
330 ident: &Ident,
331 with_namespace: bool,
332 with_schema: bool,
333 ) -> Self {
334 let schema_count = ident
335 .ns
336 .as_ref()
337 .and_then(|ns| types.modules.get(ns))
338 .map(|m| m.schema_count)
339 .unwrap_or_default();
340 let with_schema = with_schema && schema_count > 1;
341
342 let namespace = with_namespace.then_some(ident.ns).flatten();
343 let schema = with_schema
344 .then(|| types.items.get(ident))
345 .flatten()
346 .and_then(|ty| ty.schema);
347
348 match (namespace, schema) {
349 (None, None) => ModuleIdent::Root,
350 (Some(n), None) => ModuleIdent::Namespace(n),
351 (None, Some(s)) => ModuleIdent::Schema(s),
352 (Some(n), Some(s)) => ModuleIdent::Both(n, s),
353 }
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use quote::{format_ident, quote};
360
361 use super::{IdentPath, ModulePath};
362
363 #[test]
364 #[rustfmt::skip]
365 fn type_path() {
366 let string = IdentPath::from_ident(format_ident!("String"));
367 let my_type = IdentPath::from_parts(
368 [format_ident!("my_module")],
369 format_ident!("MyType"),
370 );
371 let serializer = IdentPath::from_parts(
372 [
373 format_ident!("my_module"),
374 format_ident!("quick_xml_serialize"),
375 ],
376 format_ident!("MyTypeSerializer"),
377 );
378 let deserializer = IdentPath::from_parts(
379 [
380 format_ident!("my_module"),
381 format_ident!("quick_xml_deserialize"),
382 ],
383 format_ident!("MyTypeDeserializer"),
384 );
385
386 let empty_path = ModulePath::default();
387 let module_path = ModulePath::default().join(format_ident!("my_module"));
388 let other_module_path = ModulePath::default().join(format_ident!("other_module"));
389 let serializer_path = module_path.clone().join(format_ident!("quick_xml_serialize"));
390 let deserializer_path = module_path.clone().join(format_ident!("quick_xml_deserialize"));
391
392 macro_rules! test {
393 ($actual:expr, $( $expected:tt )*) => {{
394 let a = $actual.to_string();
395 let b = quote!($( $expected )*).to_string();
396
397 assert_eq!(a, b);
398 }};
399 }
400
401 test!(string.relative_to(&empty_path), String);
404 test!(string.relative_to(&module_path), String);
405 test!(string.relative_to(&other_module_path), String);
406 test!(string.relative_to(&serializer_path), String);
407 test!(string.relative_to(&deserializer_path), String);
408
409 test!(my_type.relative_to(&empty_path), my_module::MyType);
410 test!(my_type.relative_to(&module_path), MyType);
411 test!(my_type.relative_to(&other_module_path), super::my_module::MyType);
412 test!(my_type.relative_to(&serializer_path), super::MyType);
413 test!(my_type.relative_to(&deserializer_path), super::MyType);
414
415 test!(serializer.relative_to(&empty_path), my_module::quick_xml_serialize::MyTypeSerializer);
416 test!(serializer.relative_to(&module_path), quick_xml_serialize::MyTypeSerializer);
417 test!(serializer.relative_to(&other_module_path), super::my_module::quick_xml_serialize::MyTypeSerializer);
418 test!(serializer.relative_to(&serializer_path), MyTypeSerializer);
419 test!(serializer.relative_to(&deserializer_path), super::quick_xml_serialize::MyTypeSerializer);
420
421 test!(deserializer.relative_to(&empty_path), my_module::quick_xml_deserialize::MyTypeDeserializer);
422 test!(deserializer.relative_to(&module_path), quick_xml_deserialize::MyTypeDeserializer);
423 test!(deserializer.relative_to(&other_module_path), super::my_module::quick_xml_deserialize::MyTypeDeserializer);
424 test!(deserializer.relative_to(&serializer_path), super::quick_xml_deserialize::MyTypeDeserializer);
425 test!(deserializer.relative_to(&deserializer_path), MyTypeDeserializer);
426 }
427}