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