Skip to main content

xso_proc/
types.rs

1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7//! Module with specific [`syn::Type`] constructors.
8
9use proc_macro2::Span;
10use syn::{spanned::Spanned, *};
11
12/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::Namespace`.
13pub(crate) fn namespace_ty(span: Span) -> Type {
14    Type::Path(TypePath {
15        qself: None,
16        path: Path {
17            leading_colon: Some(syn::token::PathSep {
18                spans: [span, span],
19            }),
20            segments: [
21                PathSegment {
22                    ident: Ident::new("xso", span),
23                    arguments: PathArguments::None,
24                },
25                PathSegment {
26                    ident: Ident::new("exports", span),
27                    arguments: PathArguments::None,
28                },
29                PathSegment {
30                    ident: Ident::new("rxml", span),
31                    arguments: PathArguments::None,
32                },
33                PathSegment {
34                    ident: Ident::new("Namespace", span),
35                    arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
36                        colon2_token: None,
37                        lt_token: token::Lt { spans: [span] },
38                        args: [GenericArgument::Lifetime(Lifetime {
39                            apostrophe: Span::call_site(),
40                            ident: Ident::new("static", Span::call_site()),
41                        })]
42                        .into_iter()
43                        .collect(),
44                        gt_token: token::Gt { spans: [span] },
45                    }),
46                },
47            ]
48            .into_iter()
49            .collect(),
50        },
51    })
52}
53
54/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::NcNameStr`.
55pub(crate) fn ncnamestr_ty(span: Span) -> Type {
56    Type::Path(TypePath {
57        qself: None,
58        path: Path {
59            leading_colon: Some(syn::token::PathSep {
60                spans: [span, span],
61            }),
62            segments: [
63                PathSegment {
64                    ident: Ident::new("xso", span),
65                    arguments: PathArguments::None,
66                },
67                PathSegment {
68                    ident: Ident::new("exports", span),
69                    arguments: PathArguments::None,
70                },
71                PathSegment {
72                    ident: Ident::new("rxml", span),
73                    arguments: PathArguments::None,
74                },
75                PathSegment {
76                    ident: Ident::new("NcNameStr", span),
77                    arguments: PathArguments::None,
78                },
79            ]
80            .into_iter()
81            .collect(),
82        },
83    })
84}
85
86/// Construct a [`syn::Type`] referring to `Cow<#lifetime, #ty>`.
87pub(crate) fn cow_ty(ty: Type, lifetime: Lifetime) -> Type {
88    let span = ty.span();
89    Type::Path(TypePath {
90        qself: None,
91        path: Path {
92            leading_colon: Some(syn::token::PathSep {
93                spans: [span, span],
94            }),
95            segments: [
96                PathSegment {
97                    ident: Ident::new("xso", span),
98                    arguments: PathArguments::None,
99                },
100                PathSegment {
101                    ident: Ident::new("exports", span),
102                    arguments: PathArguments::None,
103                },
104                PathSegment {
105                    ident: Ident::new("alloc", span),
106                    arguments: PathArguments::None,
107                },
108                PathSegment {
109                    ident: Ident::new("borrow", span),
110                    arguments: PathArguments::None,
111                },
112                PathSegment {
113                    ident: Ident::new("Cow", span),
114                    arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
115                        colon2_token: None,
116                        lt_token: token::Lt { spans: [span] },
117                        args: [
118                            GenericArgument::Lifetime(lifetime),
119                            GenericArgument::Type(ty),
120                        ]
121                        .into_iter()
122                        .collect(),
123                        gt_token: token::Gt { spans: [span] },
124                    }),
125                },
126            ]
127            .into_iter()
128            .collect(),
129        },
130    })
131}
132
133/// Construct a [`syn::Type`] referring to
134/// `Cow<#lifetime, ::rxml::NcNameStr>`.
135pub(crate) fn ncnamestr_cow_ty(ty_span: Span, lifetime: Lifetime) -> Type {
136    cow_ty(ncnamestr_ty(ty_span), lifetime)
137}
138
139/// Construct a [`syn::Expr`] referring to
140/// `<#ty as ::xso::FromXmlText>::from_xml_text`.
141pub(crate) fn from_xml_text_fn(ty: Type) -> Expr {
142    let span = ty.span();
143    Expr::Path(ExprPath {
144        attrs: Vec::new(),
145        qself: Some(QSelf {
146            lt_token: syn::token::Lt { spans: [span] },
147            ty: Box::new(ty),
148            position: 2,
149            as_token: Some(syn::token::As { span }),
150            gt_token: syn::token::Gt { spans: [span] },
151        }),
152        path: Path {
153            leading_colon: Some(syn::token::PathSep {
154                spans: [span, span],
155            }),
156            segments: [
157                PathSegment {
158                    ident: Ident::new("xso", span),
159                    arguments: PathArguments::None,
160                },
161                PathSegment {
162                    ident: Ident::new("FromXmlText", span),
163                    arguments: PathArguments::None,
164                },
165                PathSegment {
166                    ident: Ident::new("from_xml_text", span),
167                    arguments: PathArguments::None,
168                },
169            ]
170            .into_iter()
171            .collect(),
172        },
173    })
174}
175
176/// Construct a [`syn::Expr`] referring to
177/// `<#ty as ::xso::AsOptionalXmlText>::as_optional_xml_text`.
178pub(crate) fn as_optional_xml_text_fn(ty: Type) -> Expr {
179    let span = ty.span();
180    Expr::Path(ExprPath {
181        attrs: Vec::new(),
182        qself: Some(QSelf {
183            lt_token: syn::token::Lt { spans: [span] },
184            ty: Box::new(ty),
185            position: 2,
186            as_token: Some(syn::token::As { span }),
187            gt_token: syn::token::Gt { spans: [span] },
188        }),
189        path: Path {
190            leading_colon: Some(syn::token::PathSep {
191                spans: [span, span],
192            }),
193            segments: [
194                PathSegment {
195                    ident: Ident::new("xso", span),
196                    arguments: PathArguments::None,
197                },
198                PathSegment {
199                    ident: Ident::new("AsOptionalXmlText", span),
200                    arguments: PathArguments::None,
201                },
202                PathSegment {
203                    ident: Ident::new("as_optional_xml_text", span),
204                    arguments: PathArguments::None,
205                },
206            ]
207            .into_iter()
208            .collect(),
209        },
210    })
211}
212
213/// Construct a [`syn::Expr`] referring to
214/// `<#of_ty as ::core::default::Default>::default`.
215pub(crate) fn default_fn(of_ty: Type) -> Expr {
216    let span = of_ty.span();
217    Expr::Path(ExprPath {
218        attrs: Vec::new(),
219        qself: Some(QSelf {
220            lt_token: syn::token::Lt { spans: [span] },
221            ty: Box::new(of_ty),
222            position: 3,
223            as_token: Some(syn::token::As { span }),
224            gt_token: syn::token::Gt { spans: [span] },
225        }),
226        path: Path {
227            leading_colon: Some(syn::token::PathSep {
228                spans: [span, span],
229            }),
230            segments: [
231                PathSegment {
232                    ident: Ident::new("core", span),
233                    arguments: PathArguments::None,
234                },
235                PathSegment {
236                    ident: Ident::new("default", span),
237                    arguments: PathArguments::None,
238                },
239                PathSegment {
240                    ident: Ident::new("Default", span),
241                    arguments: PathArguments::None,
242                },
243                PathSegment {
244                    ident: Ident::new("default", span),
245                    arguments: PathArguments::None,
246                },
247            ]
248            .into_iter()
249            .collect(),
250        },
251    })
252}
253
254/// Construct a [`syn::Type`] referring to `::alloc::string::String`.
255pub(crate) fn string_ty(span: Span) -> Type {
256    Type::Path(TypePath {
257        qself: None,
258        path: Path {
259            leading_colon: Some(syn::token::PathSep {
260                spans: [span, span],
261            }),
262            segments: [
263                PathSegment {
264                    ident: Ident::new("xso", span),
265                    arguments: PathArguments::None,
266                },
267                PathSegment {
268                    ident: Ident::new("exports", span),
269                    arguments: PathArguments::None,
270                },
271                PathSegment {
272                    ident: Ident::new("alloc", span),
273                    arguments: PathArguments::None,
274                },
275                PathSegment {
276                    ident: Ident::new("string", span),
277                    arguments: PathArguments::None,
278                },
279                PathSegment {
280                    ident: Ident::new("String", span),
281                    arguments: PathArguments::None,
282                },
283            ]
284            .into_iter()
285            .collect(),
286        },
287    })
288}
289
290/// Construct a [`syn::Expr`] referring to
291/// `<#ty as ::xso::AsXmlText>::as_xml_text`.
292pub(crate) fn as_xml_text_fn(ty: Type) -> Expr {
293    let span = ty.span();
294    Expr::Path(ExprPath {
295        attrs: Vec::new(),
296        qself: Some(QSelf {
297            lt_token: syn::token::Lt { spans: [span] },
298            ty: Box::new(ty),
299            position: 2,
300            as_token: Some(syn::token::As { span }),
301            gt_token: syn::token::Gt { spans: [span] },
302        }),
303        path: Path {
304            leading_colon: Some(syn::token::PathSep {
305                spans: [span, span],
306            }),
307            segments: [
308                PathSegment {
309                    ident: Ident::new("xso", span),
310                    arguments: PathArguments::None,
311                },
312                PathSegment {
313                    ident: Ident::new("AsXmlText", span),
314                    arguments: PathArguments::None,
315                },
316                PathSegment {
317                    ident: Ident::new("as_xml_text", span),
318                    arguments: PathArguments::None,
319                },
320            ]
321            .into_iter()
322            .collect(),
323        },
324    })
325}
326
327/// Construct a [`syn::Path`] referring to `::xso::TextCodec::<#for_ty>`,
328/// returning the span of `for_ty` alongside it.
329///
330/// The span used is `codec_span`, in order to ensure that error messages
331/// about a missing implementation point at the codec, not at the type.
332fn text_codec_of(for_ty: Type, codec_span: Span) -> Path {
333    let span = codec_span;
334    Path {
335        leading_colon: Some(syn::token::PathSep {
336            spans: [span, span],
337        }),
338        segments: [
339            PathSegment {
340                ident: Ident::new("xso", span),
341                arguments: PathArguments::None,
342            },
343            PathSegment {
344                ident: Ident::new("TextCodec", span),
345                arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
346                    colon2_token: Some(syn::token::PathSep {
347                        spans: [span, span],
348                    }),
349                    lt_token: syn::token::Lt { spans: [span] },
350                    args: [GenericArgument::Type(for_ty)].into_iter().collect(),
351                    gt_token: syn::token::Gt { spans: [span] },
352                }),
353            },
354        ]
355        .into_iter()
356        .collect(),
357    }
358}
359
360/// Construct a [`syn::Expr`] referring to
361/// `::xso::TextCodec::<#for_ty>::encode`.
362///
363/// The span used is `codec_span`, in order to ensure that error messages
364/// about a missing implementation point at the codec, not at the type.
365pub(crate) fn text_codec_encode_fn(for_ty: Type, codec_span: Span) -> Expr {
366    let mut path = text_codec_of(for_ty, codec_span);
367    path.segments.push(PathSegment {
368        ident: Ident::new("encode", codec_span),
369        arguments: PathArguments::None,
370    });
371    Expr::Path(ExprPath {
372        attrs: Vec::new(),
373        qself: None,
374        path,
375    })
376}
377
378/// Construct a [`syn::Expr`] referring to
379/// `::xso::TextCodec::<#for_ty>::decode`.
380///
381/// The span used is `codec_span`, in order to ensure that error messages
382/// about a missing implementation point at the codec, not at the type.
383pub(crate) fn text_codec_decode_fn(for_ty: Type, codec_span: Span) -> Expr {
384    let mut path = text_codec_of(for_ty, codec_span);
385    path.segments.push(PathSegment {
386        ident: Ident::new("decode", codec_span),
387        arguments: PathArguments::None,
388    });
389    Expr::Path(ExprPath {
390        attrs: Vec::new(),
391        qself: None,
392        path,
393    })
394}
395
396/// Construct a [`syn::Type`] for `&#lifetime #ty`.
397pub(crate) fn ref_ty(ty: Type, lifetime: Lifetime) -> Type {
398    let span = ty.span();
399    Type::Reference(TypeReference {
400        and_token: token::And { spans: [span] },
401        lifetime: Some(lifetime),
402        mutability: None,
403        elem: Box::new(ty),
404    })
405}
406
407/// Construct a [`syn::Type`] referring to
408/// `::core::marker::PhantomData<fn() -> #ty>`.
409pub(crate) fn phantom_fn_ret_ty(ty: Type) -> Type {
410    let span = ty.span();
411    Type::Path(TypePath {
412        qself: None,
413        path: Path {
414            leading_colon: Some(syn::token::PathSep {
415                spans: [span, span],
416            }),
417            segments: [
418                PathSegment {
419                    ident: Ident::new("core", span),
420                    arguments: PathArguments::None,
421                },
422                PathSegment {
423                    ident: Ident::new("marker", span),
424                    arguments: PathArguments::None,
425                },
426                PathSegment {
427                    ident: Ident::new("PhantomData", span),
428                    arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
429                        colon2_token: None,
430                        lt_token: token::Lt { spans: [span] },
431                        args: [GenericArgument::Type(Type::BareFn(TypeBareFn {
432                            lifetimes: None,
433                            unsafety: None,
434                            abi: None,
435                            fn_token: token::Fn { span: span },
436                            paren_token: token::Paren::default(),
437                            inputs: punctuated::Punctuated::default(),
438                            variadic: None,
439                            output: ReturnType::Type(
440                                token::RArrow {
441                                    spans: [span, span],
442                                },
443                                Box::new(ty),
444                            ),
445                        }))]
446                        .into_iter()
447                        .collect(),
448                        gt_token: token::Gt { spans: [span] },
449                    }),
450                },
451            ]
452            .into_iter()
453            .collect(),
454        },
455    })
456}
457
458/// Construct a [`syn::Type`] referring to
459/// `::core::marker::PhantomData<&#lifetime ()>`.
460pub(crate) fn phantom_lifetime_ty(lifetime: Lifetime) -> Type {
461    let span = lifetime.span();
462    let dummy = Type::Tuple(TypeTuple {
463        paren_token: token::Paren::default(),
464        elems: punctuated::Punctuated::default(),
465    });
466    Type::Path(TypePath {
467        qself: None,
468        path: Path {
469            leading_colon: Some(syn::token::PathSep {
470                spans: [span, span],
471            }),
472            segments: [
473                PathSegment {
474                    ident: Ident::new("core", span),
475                    arguments: PathArguments::None,
476                },
477                PathSegment {
478                    ident: Ident::new("marker", span),
479                    arguments: PathArguments::None,
480                },
481                PathSegment {
482                    ident: Ident::new("PhantomData", span),
483                    arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
484                        colon2_token: None,
485                        lt_token: token::Lt { spans: [span] },
486                        args: [GenericArgument::Type(ref_ty(dummy, lifetime))]
487                            .into_iter()
488                            .collect(),
489                        gt_token: token::Gt { spans: [span] },
490                    }),
491                },
492            ]
493            .into_iter()
494            .collect(),
495        },
496    })
497}
498
499pub(crate) fn from_xml_trait(span: Span) -> Path {
500    Path {
501        leading_colon: Some(syn::token::PathSep {
502            spans: [span, span],
503        }),
504        segments: [
505            PathSegment {
506                ident: Ident::new("xso", span),
507                arguments: PathArguments::None,
508            },
509            PathSegment {
510                ident: Ident::new("FromXml", span),
511                arguments: PathArguments::None,
512            },
513        ]
514        .into_iter()
515        .collect(),
516    }
517}
518
519pub(crate) fn as_xml_trait(span: Span) -> Path {
520    Path {
521        leading_colon: Some(syn::token::PathSep {
522            spans: [span, span],
523        }),
524        segments: [
525            PathSegment {
526                ident: Ident::new("xso", span),
527                arguments: PathArguments::None,
528            },
529            PathSegment {
530                ident: Ident::new("AsXml", span),
531                arguments: PathArguments::None,
532            },
533        ]
534        .into_iter()
535        .collect(),
536    }
537}
538
539pub(crate) fn from_xml_text_trait(span: Span) -> Path {
540    Path {
541        leading_colon: Some(syn::token::PathSep {
542            spans: [span, span],
543        }),
544        segments: [
545            PathSegment {
546                ident: Ident::new("xso", span),
547                arguments: PathArguments::None,
548            },
549            PathSegment {
550                ident: Ident::new("FromXmlText", span),
551                arguments: PathArguments::None,
552            },
553        ]
554        .into_iter()
555        .collect(),
556    }
557}
558
559pub(crate) fn as_xml_text_trait(span: Span) -> Path {
560    Path {
561        leading_colon: Some(syn::token::PathSep {
562            spans: [span, span],
563        }),
564        segments: [
565            PathSegment {
566                ident: Ident::new("xso", span),
567                arguments: PathArguments::None,
568            },
569            PathSegment {
570                ident: Ident::new("AsXmlText", span),
571                arguments: PathArguments::None,
572            },
573        ]
574        .into_iter()
575        .collect(),
576    }
577}
578
579pub(crate) fn as_optional_xml_text_trait(span: Span) -> Path {
580    Path {
581        leading_colon: Some(syn::token::PathSep {
582            spans: [span, span],
583        }),
584        segments: [
585            PathSegment {
586                ident: Ident::new("xso", span),
587                arguments: PathArguments::None,
588            },
589            PathSegment {
590                ident: Ident::new("AsOptionalXmlText", span),
591                arguments: PathArguments::None,
592            },
593        ]
594        .into_iter()
595        .collect(),
596    }
597}
598
599/// Construct a [`syn::TypePath`] referring to
600/// `<#of_ty as ::xso::FromXml>`.
601fn from_xml_of(of_ty: Type) -> (Span, TypePath) {
602    let span = of_ty.span();
603    (
604        span,
605        TypePath {
606            qself: Some(QSelf {
607                lt_token: syn::token::Lt { spans: [span] },
608                ty: Box::new(of_ty),
609                position: 2,
610                as_token: Some(syn::token::As { span }),
611                gt_token: syn::token::Gt { spans: [span] },
612            }),
613            path: from_xml_trait(span),
614        },
615    )
616}
617
618/// Construct a [`syn::Type`] referring to
619/// `<#of_ty as ::xso::FromXml>::Builder`.
620pub(crate) fn from_xml_builder_ty(of_ty: Type) -> Type {
621    let (span, mut ty) = from_xml_of(of_ty);
622    ty.path.segments.push(PathSegment {
623        ident: Ident::new("Builder", span),
624        arguments: PathArguments::None,
625    });
626    Type::Path(ty)
627}
628
629/// Construct a [`syn::Expr`] referring to
630/// `<#of_ty as ::xso::FromXml>::from_events`.
631pub(crate) fn from_events_fn(of_ty: Type) -> Expr {
632    let (span, mut ty) = from_xml_of(of_ty);
633    ty.path.segments.push(PathSegment {
634        ident: Ident::new("from_events", span),
635        arguments: PathArguments::None,
636    });
637    Expr::Path(ExprPath {
638        attrs: Vec::new(),
639        qself: ty.qself,
640        path: ty.path,
641    })
642}
643
644/// Construct a [`syn::Type`] which wraps the given `ty` in
645/// `::core::option::Option<_>`.
646pub(crate) fn option_ty(ty: Type) -> Type {
647    let span = ty.span();
648    Type::Path(TypePath {
649        qself: None,
650        path: Path {
651            leading_colon: Some(syn::token::PathSep {
652                spans: [span, span],
653            }),
654            segments: [
655                PathSegment {
656                    ident: Ident::new("core", span),
657                    arguments: PathArguments::None,
658                },
659                PathSegment {
660                    ident: Ident::new("option", span),
661                    arguments: PathArguments::None,
662                },
663                PathSegment {
664                    ident: Ident::new("Option", span),
665                    arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
666                        colon2_token: None,
667                        lt_token: syn::token::Lt { spans: [span] },
668                        args: [GenericArgument::Type(ty)].into_iter().collect(),
669                        gt_token: syn::token::Gt { spans: [span] },
670                    }),
671                },
672            ]
673            .into_iter()
674            .collect(),
675        },
676    })
677}
678
679/// Construct a [`syn::TypePath`] referring to
680/// `<#of_ty as ::xso::FromEventsBuilder>`.
681fn from_events_builder_of(of_ty: Type) -> (Span, TypePath) {
682    let span = of_ty.span();
683    (
684        span,
685        TypePath {
686            qself: Some(QSelf {
687                lt_token: syn::token::Lt { spans: [span] },
688                ty: Box::new(of_ty),
689                position: 2,
690                as_token: Some(syn::token::As { span }),
691                gt_token: syn::token::Gt { spans: [span] },
692            }),
693            path: Path {
694                leading_colon: Some(syn::token::PathSep {
695                    spans: [span, span],
696                }),
697                segments: [
698                    PathSegment {
699                        ident: Ident::new("xso", span),
700                        arguments: PathArguments::None,
701                    },
702                    PathSegment {
703                        ident: Ident::new("FromEventsBuilder", span),
704                        arguments: PathArguments::None,
705                    },
706                ]
707                .into_iter()
708                .collect(),
709            },
710        },
711    )
712}
713
714/// Construct a [`syn::Expr`] referring to
715/// `<#of_ty as ::xso::FromEventsBuilder>::feed`.
716pub(crate) fn feed_fn(of_ty: Type) -> Expr {
717    let (span, mut ty) = from_events_builder_of(of_ty);
718    ty.path.segments.push(PathSegment {
719        ident: Ident::new("feed", span),
720        arguments: PathArguments::None,
721    });
722    Expr::Path(ExprPath {
723        attrs: Vec::new(),
724        qself: ty.qself,
725        path: ty.path,
726    })
727}
728
729fn as_xml_of(of_ty: Type) -> (Span, TypePath) {
730    let span = of_ty.span();
731    (
732        span,
733        TypePath {
734            qself: Some(QSelf {
735                lt_token: syn::token::Lt { spans: [span] },
736                ty: Box::new(of_ty),
737                position: 2,
738                as_token: Some(syn::token::As { span }),
739                gt_token: syn::token::Gt { spans: [span] },
740            }),
741            path: as_xml_trait(span),
742        },
743    )
744}
745
746/// Construct a [`syn::Expr`] referring to
747/// `<#of_ty as ::xso::AsXml>::as_xml_iter`.
748pub(crate) fn as_xml_iter_fn(of_ty: Type) -> Expr {
749    let (span, mut ty) = as_xml_of(of_ty);
750    ty.path.segments.push(PathSegment {
751        ident: Ident::new("as_xml_iter", span),
752        arguments: PathArguments::None,
753    });
754    Expr::Path(ExprPath {
755        attrs: Vec::new(),
756        qself: ty.qself,
757        path: ty.path,
758    })
759}
760
761/// Construct a [`syn::Type`] referring to
762/// `<#of_ty as ::xso::AsXml>::ItemIter`.
763pub(crate) fn item_iter_ty(of_ty: Type, lifetime: Lifetime) -> Type {
764    let (span, mut ty) = as_xml_of(of_ty);
765    ty.path.segments.push(PathSegment {
766        ident: Ident::new("ItemIter", span),
767        arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
768            colon2_token: None,
769            lt_token: token::Lt { spans: [span] },
770            args: [GenericArgument::Lifetime(lifetime)].into_iter().collect(),
771            gt_token: token::Gt { spans: [span] },
772        }),
773    });
774    Type::Path(ty)
775}
776
777/// Construct a [`syn::TypePath`] referring to `<#of_ty as IntoIterator>`.
778fn into_iterator_of(of_ty: Type) -> (Span, TypePath) {
779    let span = of_ty.span();
780    (
781        span,
782        TypePath {
783            qself: Some(QSelf {
784                lt_token: syn::token::Lt { spans: [span] },
785                ty: Box::new(of_ty),
786                position: 3,
787                as_token: Some(syn::token::As { span }),
788                gt_token: syn::token::Gt { spans: [span] },
789            }),
790            path: Path {
791                leading_colon: Some(syn::token::PathSep {
792                    spans: [span, span],
793                }),
794                segments: [
795                    PathSegment {
796                        ident: Ident::new("core", span),
797                        arguments: PathArguments::None,
798                    },
799                    PathSegment {
800                        ident: Ident::new("iter", span),
801                        arguments: PathArguments::None,
802                    },
803                    PathSegment {
804                        ident: Ident::new("IntoIterator", span),
805                        arguments: PathArguments::None,
806                    },
807                ]
808                .into_iter()
809                .collect(),
810            },
811        },
812    )
813}
814
815/// Construct a [`syn::Type`] referring to
816/// `<#of_ty as IntoIterator>::IntoIter`.
817pub(crate) fn into_iterator_iter_ty(of_ty: Type) -> Type {
818    let (span, mut ty) = into_iterator_of(of_ty);
819    ty.path.segments.push(PathSegment {
820        ident: Ident::new("IntoIter", span),
821        arguments: PathArguments::None,
822    });
823    Type::Path(ty)
824}
825
826/// Construct a [`syn::Type`] referring to
827/// `<#of_ty as IntoIterator>::Item`.
828pub(crate) fn into_iterator_item_ty(of_ty: Type) -> Type {
829    let (span, mut ty) = into_iterator_of(of_ty);
830    ty.path.segments.push(PathSegment {
831        ident: Ident::new("Item", span),
832        arguments: PathArguments::None,
833    });
834    Type::Path(ty)
835}
836
837/// Construct a [`syn::Expr`] referring to
838/// `<#of_ty as IntoIterator>::into_iter`.
839pub(crate) fn into_iterator_into_iter_fn(of_ty: Type) -> Expr {
840    let (span, mut ty) = into_iterator_of(of_ty);
841    ty.path.segments.push(PathSegment {
842        ident: Ident::new("into_iter", span),
843        arguments: PathArguments::None,
844    });
845    Expr::Path(ExprPath {
846        attrs: Vec::new(),
847        qself: ty.qself,
848        path: ty.path,
849    })
850}
851
852/// Construct a [`syn::Expr`] referring to
853/// `<#of_ty as ::core::iter::Extend>::extend`.
854pub(crate) fn extend_fn(of_ty: Type, item_ty: Type) -> Expr {
855    let span = of_ty.span();
856    Expr::Path(ExprPath {
857        attrs: Vec::new(),
858        qself: Some(QSelf {
859            lt_token: syn::token::Lt { spans: [span] },
860            ty: Box::new(of_ty),
861            position: 3,
862            as_token: Some(syn::token::As { span }),
863            gt_token: syn::token::Gt { spans: [span] },
864        }),
865        path: Path {
866            leading_colon: Some(syn::token::PathSep {
867                spans: [span, span],
868            }),
869            segments: [
870                PathSegment {
871                    ident: Ident::new("core", span),
872                    arguments: PathArguments::None,
873                },
874                PathSegment {
875                    ident: Ident::new("iter", span),
876                    arguments: PathArguments::None,
877                },
878                PathSegment {
879                    ident: Ident::new("Extend", span),
880                    arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
881                        colon2_token: Some(syn::token::PathSep {
882                            spans: [span, span],
883                        }),
884                        lt_token: syn::token::Lt { spans: [span] },
885                        args: [GenericArgument::Type(item_ty)].into_iter().collect(),
886                        gt_token: syn::token::Gt { spans: [span] },
887                    }),
888                },
889                PathSegment {
890                    ident: Ident::new("extend", span),
891                    arguments: PathArguments::None,
892                },
893            ]
894            .into_iter()
895            .collect(),
896        },
897    })
898}
899
900/// Construct a [`syn::Type`] referring to `xso::OptionAsXml<#inner_ty>`.
901pub(crate) fn option_as_xml_ty(inner_ty: Type) -> Type {
902    let span = inner_ty.span();
903    Type::Path(TypePath {
904        qself: None,
905        path: Path {
906            leading_colon: Some(syn::token::PathSep {
907                spans: [span, span],
908            }),
909            segments: [
910                PathSegment {
911                    ident: Ident::new("xso", span),
912                    arguments: PathArguments::None,
913                },
914                PathSegment {
915                    ident: Ident::new("asxml", span),
916                    arguments: PathArguments::None,
917                },
918                PathSegment {
919                    ident: Ident::new("OptionAsXml", span),
920                    arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
921                        colon2_token: None,
922                        lt_token: token::Lt { spans: [span] },
923                        args: [GenericArgument::Type(inner_ty)].into_iter().collect(),
924                        gt_token: token::Gt { spans: [span] },
925                    }),
926                },
927            ]
928            .into_iter()
929            .collect(),
930        },
931    })
932}
933
934/// Construct a [`syn::Type`] referring to `::xso::exports::minidom::Element`.
935#[cfg(feature = "minidom")]
936pub(crate) fn element_ty(span: Span) -> Type {
937    Type::Path(TypePath {
938        qself: None,
939        path: Path {
940            leading_colon: Some(syn::token::PathSep {
941                spans: [span, span],
942            }),
943            segments: [
944                PathSegment {
945                    ident: Ident::new("xso", span),
946                    arguments: PathArguments::None,
947                },
948                PathSegment {
949                    ident: Ident::new("exports", span),
950                    arguments: PathArguments::None,
951                },
952                PathSegment {
953                    ident: Ident::new("minidom", span),
954                    arguments: PathArguments::None,
955                },
956                PathSegment {
957                    ident: Ident::new("Element", span),
958                    arguments: PathArguments::None,
959                },
960            ]
961            .into_iter()
962            .collect(),
963        },
964    })
965}
966
967/// Construct a [`syn::Path`] referring to `::xso::UnknownAttributePolicy`.
968pub(crate) fn unknown_attribute_policy_path(span: Span) -> Path {
969    Path {
970        leading_colon: Some(syn::token::PathSep {
971            spans: [span, span],
972        }),
973        segments: [
974            PathSegment {
975                ident: Ident::new("xso", span),
976                arguments: PathArguments::None,
977            },
978            PathSegment {
979                ident: Ident::new("UnknownAttributePolicy", span),
980                arguments: PathArguments::None,
981            },
982        ]
983        .into_iter()
984        .collect(),
985    }
986}
987
988/// Construct a [`syn::Path`] referring to `::xso::UnknownChildPolicy`.
989pub(crate) fn unknown_child_policy_path(span: Span) -> Path {
990    Path {
991        leading_colon: Some(syn::token::PathSep {
992            spans: [span, span],
993        }),
994        segments: [
995            PathSegment {
996                ident: Ident::new("xso", span),
997                arguments: PathArguments::None,
998            },
999            PathSegment {
1000                ident: Ident::new("UnknownChildPolicy", span),
1001                arguments: PathArguments::None,
1002            },
1003        ]
1004        .into_iter()
1005        .collect(),
1006    }
1007}
1008
1009/// Construct a [`syn::Type`] referring to `::xso::fromxml::Discard`.
1010pub(crate) fn discard_builder_ty(span: Span) -> Type {
1011    Type::Path(TypePath {
1012        qself: None,
1013        path: Path {
1014            leading_colon: Some(syn::token::PathSep {
1015                spans: [span, span],
1016            }),
1017            segments: [
1018                PathSegment {
1019                    ident: Ident::new("xso", span),
1020                    arguments: PathArguments::None,
1021                },
1022                PathSegment {
1023                    ident: Ident::new("fromxml", span),
1024                    arguments: PathArguments::None,
1025                },
1026                PathSegment {
1027                    ident: Ident::new("Discard", span),
1028                    arguments: PathArguments::None,
1029                },
1030            ]
1031            .into_iter()
1032            .collect(),
1033        },
1034    })
1035}
1036
1037/// Construct a [`syn::Type`] referring to the built-in `bool` type.
1038///
1039/// Note that we go through `xso::exports::CoreBool` for that, because there seems
1040/// to be no way to access built-in types once they have been shadowed in a
1041/// scope.
1042pub(crate) fn bool_ty(span: Span) -> Type {
1043    Type::Path(TypePath {
1044        qself: None,
1045        path: Path {
1046            leading_colon: Some(syn::token::PathSep {
1047                spans: [span, span],
1048            }),
1049            segments: [
1050                PathSegment {
1051                    ident: Ident::new("xso", span),
1052                    arguments: PathArguments::None,
1053                },
1054                PathSegment {
1055                    ident: Ident::new("exports", span),
1056                    arguments: PathArguments::None,
1057                },
1058                PathSegment {
1059                    ident: Ident::new("CoreBool", span),
1060                    arguments: PathArguments::None,
1061                },
1062            ]
1063            .into_iter()
1064            .collect(),
1065        },
1066    })
1067}
1068
1069/// Construct a [`syn::Type`] referring to the built-in `u8` type.
1070///
1071/// Note that we go through `xso::exports::CoreU8` for that, because there seems
1072/// to be no way to access built-in types once they have been shadowed in a
1073/// scope.
1074pub(crate) fn u8_ty(span: Span) -> Type {
1075    Type::Path(TypePath {
1076        qself: None,
1077        path: Path {
1078            leading_colon: Some(syn::token::PathSep {
1079                spans: [span, span],
1080            }),
1081            segments: [
1082                PathSegment {
1083                    ident: Ident::new("xso", span),
1084                    arguments: PathArguments::None,
1085                },
1086                PathSegment {
1087                    ident: Ident::new("exports", span),
1088                    arguments: PathArguments::None,
1089                },
1090                PathSegment {
1091                    ident: Ident::new("CoreU8", span),
1092                    arguments: PathArguments::None,
1093                },
1094            ]
1095            .into_iter()
1096            .collect(),
1097        },
1098    })
1099}
1100
1101/// Construct a [`syn::Type`] referring to `::xso::fromxml::EmptyBuilder`.
1102pub(crate) fn empty_builder_ty(span: Span) -> Type {
1103    Type::Path(TypePath {
1104        qself: None,
1105        path: Path {
1106            leading_colon: Some(syn::token::PathSep {
1107                spans: [span, span],
1108            }),
1109            segments: [
1110                PathSegment {
1111                    ident: Ident::new("xso", span),
1112                    arguments: PathArguments::None,
1113                },
1114                PathSegment {
1115                    ident: Ident::new("fromxml", span),
1116                    arguments: PathArguments::None,
1117                },
1118                PathSegment {
1119                    ident: Ident::new("EmptyBuilder", span),
1120                    arguments: PathArguments::None,
1121                },
1122            ]
1123            .into_iter()
1124            .collect(),
1125        },
1126    })
1127}
1128
1129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1130pub(crate) enum ReferencedIdent<'x> {
1131    Lifetime(&'x Ident),
1132    Name(&'x Ident),
1133}
1134
1135pub(crate) fn iter_referenced_names_in_bound<'a, F: FnMut(ReferencedIdent<'a>) -> Result<()>>(
1136    bound: &'a TypeParamBound,
1137    f: &mut F,
1138) -> Result<()> {
1139    match bound {
1140        TypeParamBound::Trait(bound) => {
1141            if let Some(lifetimes) = bound.lifetimes.as_ref() {
1142                return Err(Error::new_spanned(
1143                    lifetimes,
1144                    "use of higher-rank trait bounds is not supported",
1145                ));
1146            }
1147            iter_referenced_names_in_path(&bound.path, f)?;
1148        }
1149        TypeParamBound::Lifetime(bound) => {
1150            f(ReferencedIdent::Lifetime(&bound.ident))?;
1151        }
1152        other => {
1153            return Err(Error::new_spanned(
1154                other,
1155                "this kind of type parameter bound is not supported",
1156            ))
1157        }
1158    }
1159    Ok(())
1160}
1161
1162pub(crate) fn iter_referenced_names_in_generic_param<
1163    'a,
1164    F: FnMut(ReferencedIdent<'a>) -> Result<()>,
1165>(
1166    param: &'a GenericParam,
1167    f: &mut F,
1168) -> Result<()> {
1169    match param {
1170        GenericParam::Lifetime(param) => {
1171            for bound in param.bounds.iter() {
1172                f(ReferencedIdent::Lifetime(&bound.ident))?;
1173            }
1174        }
1175        GenericParam::Type(param) => {
1176            for bound in param.bounds.iter() {
1177                iter_referenced_names_in_bound(bound, f)?;
1178            }
1179            if let Some(default) = param.default.as_ref() {
1180                iter_referenced_names_in_ty(default, f)?;
1181            }
1182        }
1183        GenericParam::Const(param) => {
1184            iter_referenced_names_in_ty(&param.ty, f)?;
1185            if let Some(default) = param.default.as_ref() {
1186                iter_referenced_names_in_expr(default, f)?;
1187            }
1188        }
1189    }
1190    Ok(())
1191}
1192
1193pub(crate) fn iter_referenced_names_in_ty<'a, F: FnMut(ReferencedIdent<'a>) -> Result<()>>(
1194    ty: &'a Type,
1195    f: &mut F,
1196) -> Result<()> {
1197    match ty {
1198        Type::Array(ty) => iter_referenced_names_in_ty(&ty.elem, f)?,
1199        Type::BareFn(ty) => {
1200            match ty.output {
1201                ReturnType::Type(_, ref ty) => iter_referenced_names_in_ty(ty, f)?,
1202                ReturnType::Default => (),
1203            }
1204
1205            for input in ty.inputs.iter() {
1206                iter_referenced_names_in_ty(&input.ty, f)?;
1207            }
1208        }
1209        Type::Group(ty) => iter_referenced_names_in_ty(&ty.elem, f)?,
1210        Type::ImplTrait(ty) => {
1211            for bound in ty.bounds.iter() {
1212                iter_referenced_names_in_bound(&bound, f)?;
1213            }
1214        }
1215        Type::Infer(_) => (),
1216        Type::Never(_) => (),
1217        Type::Paren(ty) => iter_referenced_names_in_ty(&ty.elem, f)?,
1218        Type::Path(ty) => {
1219            if let Some(qself) = ty.qself.as_ref() {
1220                iter_referenced_names_in_ty(&qself.ty, f)?;
1221            }
1222            iter_referenced_names_in_path(&ty.path, f)?;
1223        }
1224        Type::Ptr(ty) => iter_referenced_names_in_ty(&ty.elem, f)?,
1225        Type::Reference(ty) => {
1226            if let Some(lifetime) = ty.lifetime.as_ref() {
1227                f(ReferencedIdent::Lifetime(&lifetime.ident))?;
1228            }
1229            iter_referenced_names_in_ty(&ty.elem, f)?;
1230        }
1231        Type::Slice(ty) => iter_referenced_names_in_ty(&ty.elem, f)?,
1232        Type::TraitObject(ty) => {
1233            for bound in ty.bounds.iter() {
1234                iter_referenced_names_in_bound(&bound, f)?;
1235            }
1236        }
1237        Type::Tuple(ty) => {
1238            for elem in ty.elems.iter() {
1239                iter_referenced_names_in_ty(elem, f)?;
1240            }
1241        }
1242        other => {
1243            return Err(Error::new_spanned(
1244                other,
1245                "this kind of type is not supported",
1246            ))
1247        }
1248    }
1249    Ok(())
1250}
1251
1252fn iter_referenced_names_in_angle_bracketed_generic_arguments<
1253    'a,
1254    F: FnMut(ReferencedIdent<'a>) -> Result<()>,
1255>(
1256    args: &'a AngleBracketedGenericArguments,
1257    f: &mut F,
1258) -> Result<()> {
1259    for arg in args.args.iter() {
1260        match arg {
1261            GenericArgument::Lifetime(arg) => f(ReferencedIdent::Name(&arg.ident))?,
1262            GenericArgument::Type(arg) => iter_referenced_names_in_ty(&arg, f)?,
1263            GenericArgument::Const(arg) => iter_referenced_names_in_expr(&arg, f)?,
1264            GenericArgument::AssocType(arg) => iter_referenced_names_in_ty(&arg.ty, f)?,
1265            GenericArgument::AssocConst(arg) => iter_referenced_names_in_expr(&arg.value, f)?,
1266            GenericArgument::Constraint(arg) => {
1267                for bound in arg.bounds.iter() {
1268                    iter_referenced_names_in_bound(&bound, f)?;
1269                }
1270            }
1271            other => {
1272                return Err(Error::new_spanned(
1273                    other,
1274                    "this kind of generic argument is not supported",
1275                ))
1276            }
1277        }
1278    }
1279    Ok(())
1280}
1281
1282pub(crate) fn iter_referenced_names_in_path<'a, F: FnMut(ReferencedIdent<'a>) -> Result<()>>(
1283    path: &'a Path,
1284    f: &mut F,
1285) -> Result<()> {
1286    if path.leading_colon.is_none() {
1287        // first item in path is local, consider it.
1288        if let Some(first) = path.segments.first() {
1289            f(ReferencedIdent::Name(&first.ident))?;
1290        }
1291    }
1292
1293    for segment in path.segments.iter() {
1294        match segment.arguments {
1295            PathArguments::None => (),
1296            PathArguments::AngleBracketed(ref arguments) => {
1297                iter_referenced_names_in_angle_bracketed_generic_arguments(&arguments, f)?;
1298            }
1299            PathArguments::Parenthesized(ref arguments) => {
1300                for arg in arguments.inputs.iter() {
1301                    iter_referenced_names_in_ty(&arg, f)?;
1302                }
1303                match arguments.output {
1304                    ReturnType::Type(_, ref ty) => iter_referenced_names_in_ty(&ty, f)?,
1305                    ReturnType::Default => (),
1306                }
1307            }
1308        }
1309    }
1310    Ok(())
1311}
1312
1313pub(crate) fn iter_referenced_names_in_stmt<'a, F: FnMut(ReferencedIdent<'a>) -> Result<()>>(
1314    stmt: &'a Stmt,
1315    f: &mut F,
1316) -> Result<()> {
1317    match stmt {
1318        Stmt::Expr(expr, _) => {
1319            iter_referenced_names_in_expr(&expr, f)?;
1320        }
1321        other => {
1322            return Err(Error::new_spanned(
1323                other,
1324                "this kind of statement is not supported",
1325            ))
1326        }
1327    }
1328    Ok(())
1329}
1330
1331pub(crate) fn iter_referenced_names_in_expr<'a, F: FnMut(ReferencedIdent<'a>) -> Result<()>>(
1332    expr: &'a Expr,
1333    f: &mut F,
1334) -> Result<()> {
1335    match expr {
1336        Expr::Array(expr) => {
1337            for elem in expr.elems.iter() {
1338                iter_referenced_names_in_expr(&elem, f)?;
1339            }
1340        }
1341        Expr::Binary(expr) => {
1342            iter_referenced_names_in_expr(&expr.left, f)?;
1343            iter_referenced_names_in_expr(&expr.right, f)?;
1344        }
1345        Expr::Block(expr) => {
1346            for stmt in expr.block.stmts.iter() {
1347                iter_referenced_names_in_stmt(&stmt, f)?;
1348            }
1349        }
1350        Expr::Break(_) => (),
1351        Expr::Call(expr) => {
1352            iter_referenced_names_in_expr(&expr.func, f)?;
1353            for arg in expr.args.iter() {
1354                iter_referenced_names_in_expr(&arg, f)?;
1355            }
1356        }
1357        Expr::Cast(expr) => {
1358            iter_referenced_names_in_expr(&expr.expr, f)?;
1359            iter_referenced_names_in_ty(&expr.ty, f)?;
1360        }
1361        // Expr::Closure: binds new names, will cause confusion.
1362        Expr::Const(expr) => {
1363            for stmt in expr.block.stmts.iter() {
1364                iter_referenced_names_in_stmt(&stmt, f)?;
1365            }
1366        }
1367        Expr::Continue(_) => (),
1368        Expr::Field(expr) => {
1369            iter_referenced_names_in_expr(&expr.base, f)?;
1370        }
1371        // Expr::For: binds new names, will cause confusion.
1372        Expr::Group(expr) => {
1373            iter_referenced_names_in_expr(&expr.expr, f)?;
1374        }
1375        Expr::If(expr) => {
1376            iter_referenced_names_in_expr(&expr.cond, f)?;
1377            for stmt in expr.then_branch.stmts.iter() {
1378                iter_referenced_names_in_stmt(&stmt, f)?;
1379            }
1380            if let Some(else_branch) = expr.else_branch.as_ref() {
1381                iter_referenced_names_in_expr(&else_branch.1, f)?;
1382            }
1383        }
1384        Expr::Index(expr) => {
1385            iter_referenced_names_in_expr(&expr.expr, f)?;
1386        }
1387        // Expr::Let: binds new names, will cause confusion.
1388        Expr::Lit(_) => (),
1389        Expr::Loop(expr) => {
1390            for stmt in expr.body.stmts.iter() {
1391                iter_referenced_names_in_stmt(&stmt, f)?;
1392            }
1393        }
1394        // Expr::Macro: anything can happen
1395        Expr::MethodCall(expr) => {
1396            iter_referenced_names_in_expr(&expr.receiver, f)?;
1397            iter_referenced_names_in_expr(&expr.receiver, f)?;
1398        }
1399        other => {
1400            return Err(Error::new_spanned(
1401                other,
1402                "this kind of expression is not supported",
1403            ))
1404        }
1405    }
1406    Ok(())
1407}