Skip to main content

xsd_parser/pipeline/renderer/steps/quick_xml/
collect_namespaces.rs

1use proc_macro2::{Ident as Ident2, TokenStream};
2use quote::{format_ident, quote};
3
4use crate::config::TypedefMode;
5use crate::models::data::TagName;
6use crate::models::{
7    code::IdentPath,
8    data::{
9        ComplexBase, ComplexData, ComplexDataAttribute, ComplexDataElement, ComplexDataEnum,
10        ComplexDataStruct, DynamicData, EnumerationData, Occurs, ReferenceData, SimpleData,
11        StructMode, UnionData,
12    },
13    schema::xs::FormChoiceType,
14};
15
16use super::super::super::{
17    context::Context, DataTypeVariant, MetaData, RenderStep, RenderStepType,
18};
19
20macro_rules! resolve_ident {
21    ($ctx:ident, $path:expr) => {
22        $ctx.resolve_ident_path($path)
23    };
24}
25
26/// Implements a [`RenderStep`] that renders the [`CollectNamespaces`](xsd_parser_types::quick_xml::CollectNamespaces)
27/// trait for every generated type.
28///
29/// This is required when using [`NamespaceSerialization::Dynamic`](super::NamespaceSerialization::Dynamic)
30/// so that the serializer can traverse the value tree at runtime to discover which
31/// XML namespaces are actually needed before emitting the root start element.
32#[derive(Debug, Clone, Copy)]
33pub struct QuickXmlCollectNamespacesRenderStep;
34
35impl RenderStep for QuickXmlCollectNamespacesRenderStep {
36    fn render_step_type(&self) -> RenderStepType {
37        RenderStepType::ExtraImpls
38    }
39
40    fn initialize(&mut self, meta: &mut MetaData<'_>) {
41        let ident = IdentPath::from_parts(
42            [meta.xsd_parser_types.clone(), format_ident!("quick_xml")],
43            format_ident!("CollectNamespaces"),
44        );
45
46        if !meta.dyn_type_traits.contains(&ident) {
47            meta.dyn_type_traits.push(ident);
48        }
49    }
50
51    fn render_type(&mut self, ctx: &mut Context<'_, '_>) {
52        match &ctx.data.variant {
53            DataTypeVariant::BuildIn(_) | DataTypeVariant::Custom(_) => (),
54            DataTypeVariant::Union(x) => x.render_collect_namespaces(ctx),
55            DataTypeVariant::Dynamic(x) => x.render_collect_namespaces(ctx),
56            DataTypeVariant::Reference(x) => x.render_collect_namespaces(ctx),
57            DataTypeVariant::Enumeration(x) => x.render_collect_namespaces(ctx),
58            DataTypeVariant::Simple(x) => x.render_collect_namespaces(ctx),
59            DataTypeVariant::Complex(x) => x.render_collect_namespaces(ctx),
60        }
61    }
62}
63
64/* UnionData */
65
66impl UnionData<'_> {
67    pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
68        ctx.render_collect_namespaces_noop(&self.type_ident);
69    }
70}
71
72/* DynamicData */
73
74impl DynamicData<'_> {
75    pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
76        let Self { type_ident, .. } = self;
77
78        let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart");
79        let collect_namespaces =
80            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces");
81        let serialize_helper =
82            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper");
83
84        let code = quote! {
85            impl #collect_namespaces for #type_ident {
86                fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) {
87                    self.0.collect_namespaces(helper, bytes);
88                }
89            }
90        };
91
92        ctx.current_module().append(code);
93    }
94}
95
96/* ReferenceData */
97
98impl ReferenceData<'_> {
99    pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
100        let Self {
101            mode, type_ident, ..
102        } = self;
103
104        if matches!(mode, TypedefMode::Auto | TypedefMode::Typedef) {
105            return;
106        }
107
108        let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart");
109        let collect_namespaces =
110            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces");
111        let serialize_helper =
112            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper");
113
114        let code = quote! {
115            impl #collect_namespaces for #type_ident {
116                fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) {
117                    self.0.collect_namespaces(helper, bytes);
118                }
119            }
120        };
121
122        ctx.current_module().append(code);
123    }
124}
125
126/* EnumerationData */
127
128impl EnumerationData<'_> {
129    pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
130        ctx.render_collect_namespaces_noop(&self.type_ident);
131    }
132}
133
134/* SimpleData */
135
136impl SimpleData<'_> {
137    pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
138        ctx.render_collect_namespaces_noop(&self.type_ident);
139    }
140}
141
142/* ComplexData */
143
144impl ComplexData<'_> {
145    pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
146        match self {
147            Self::Enum {
148                type_,
149                content_type,
150            } => {
151                type_.render_collect_namespaces(ctx);
152
153                if let Some(content_type) = content_type {
154                    content_type.render_collect_namespaces(ctx);
155                }
156            }
157            Self::Struct {
158                type_,
159                content_type,
160            } => {
161                type_.render_collect_namespaces(ctx);
162
163                if let Some(content_type) = content_type {
164                    content_type.render_collect_namespaces(ctx);
165                }
166            }
167        }
168    }
169}
170
171impl ComplexDataEnum<'_> {
172    fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
173        let type_ident = &self.type_ident;
174
175        let own_ns = self.render_collect_own_namespace(ctx);
176
177        let variants = self
178            .elements
179            .iter()
180            .map(ComplexDataElement::render_collect_namespaces_match_arm);
181
182        let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart");
183        let collect_namespaces =
184            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces");
185        let serialize_helper =
186            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper");
187
188        let code = quote! {
189            impl #collect_namespaces for #type_ident {
190                fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) {
191                    #own_ns
192                    match self {
193                        #( #variants )*
194                    }
195                }
196            }
197        };
198
199        ctx.current_module().append(code);
200    }
201}
202
203impl ComplexDataStruct<'_> {
204    fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) {
205        let type_ident = &self.type_ident;
206
207        let own_ns = self.render_collect_own_namespace(ctx);
208        let attrib_ns = self.render_collect_attribute_namespaces(ctx);
209
210        let field_calls: Vec<_> = match &self.mode {
211            StructMode::Empty { .. } => Vec::new(),
212            StructMode::Content { .. } => {
213                vec![quote! {
214                    self.content.collect_namespaces(helper, bytes);
215                }]
216            }
217            StructMode::All { elements, .. } | StructMode::Sequence { elements, .. } => elements
218                .iter()
219                .map(ComplexDataElement::render_collect_namespaces_field_call)
220                .collect(),
221        };
222
223        let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart");
224        let collect_namespaces =
225            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces");
226        let serialize_helper =
227            resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper");
228
229        let code = quote! {
230            impl #collect_namespaces for #type_ident {
231                fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) {
232                    #own_ns
233                    #( #attrib_ns )*
234                    #( #field_calls )*
235                }
236            }
237        };
238
239        ctx.current_module().append(code);
240    }
241}
242
243impl ComplexBase<'_> {
244    fn render_collect_own_namespace(&self, ctx: &Context<'_, '_>) -> Option<TokenStream> {
245        self.tag_name.as_ref()?.render_collect_namespace(ctx)
246    }
247}
248
249impl ComplexDataStruct<'_> {
250    fn render_collect_attribute_namespaces(&self, ctx: &Context<'_, '_>) -> Vec<TokenStream> {
251        self.attributes
252            .iter()
253            .filter_map(|attrib| attrib.render_collect_attribute_namespace(ctx))
254            .collect()
255    }
256}
257
258impl ComplexDataAttribute<'_> {
259    fn render_collect_attribute_namespace(&self, ctx: &Context<'_, '_>) -> Option<TokenStream> {
260        self.tag_name.render_collect_namespace(ctx)
261    }
262}
263
264impl ComplexDataElement<'_> {
265    fn render_collect_namespaces_field_call(&self) -> TokenStream {
266        let field_ident = &self.field_ident;
267
268        match self.occurs {
269            Occurs::None => unreachable!(),
270            Occurs::Single => quote! {
271                self.#field_ident.collect_namespaces(helper, bytes);
272            },
273            Occurs::Optional => quote! {
274                if let Some(inner) = &self.#field_ident {
275                    inner.collect_namespaces(helper, bytes);
276                }
277            },
278            Occurs::DynamicList | Occurs::StaticList(_) => quote! {
279                for item in &self.#field_ident {
280                    item.collect_namespaces(helper, bytes);
281                }
282            },
283        }
284    }
285
286    fn render_collect_namespaces_match_arm(&self) -> TokenStream {
287        let variant_ident = &self.variant_ident;
288
289        match self.occurs {
290            Occurs::None => unreachable!(),
291            Occurs::Single => quote! {
292                Self::#variant_ident(x) => x.collect_namespaces(helper, bytes),
293            },
294            Occurs::Optional => quote! {
295                Self::#variant_ident(x) => {
296                    if let Some(inner) = x {
297                        inner.collect_namespaces(helper, bytes);
298                    }
299                }
300            },
301            Occurs::DynamicList | Occurs::StaticList(_) => quote! {
302                Self::#variant_ident(x) => {
303                    for item in x.iter() {
304                        item.collect_namespaces(helper, bytes);
305                    }
306                }
307            },
308        }
309    }
310}
311
312/* Misc */
313
314impl Context<'_, '_> {
315    fn render_collect_namespaces_noop(&mut self, type_ident: &Ident2) {
316        let bytes_start = resolve_ident!(self, "::xsd_parser_types::quick_xml::BytesStart");
317        let collect_namespaces =
318            resolve_ident!(self, "::xsd_parser_types::quick_xml::CollectNamespaces");
319        let serialize_helper =
320            resolve_ident!(self, "::xsd_parser_types::quick_xml::SerializeHelper");
321
322        let code = quote! {
323            impl #collect_namespaces for #type_ident {
324                fn collect_namespaces(&self, _helper: &mut #serialize_helper, _bytes: &mut #bytes_start<'_>) { }
325            }
326        };
327
328        self.current_module().append(code);
329    }
330}
331
332impl TagName<'_> {
333    fn render_collect_namespace(&self, ctx: &Context<'_, '_>) -> Option<TokenStream> {
334        if self.form != FormChoiceType::Qualified {
335            return None;
336        }
337
338        let module = self.module?;
339        let ns = module.make_ns_const()?;
340        let ns_const = ctx.resolve_type_for_module(&ns);
341
342        let prefix = module.make_prefix_const()?;
343        let prefix_const = ctx.resolve_type_for_module(&prefix);
344
345        Some(quote! {
346            helper.write_xmlns(bytes, Some(&#prefix_const), &#ns_const);
347        })
348    }
349}