xsd_parser/pipeline/renderer/
context.rs

1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::ops::Deref;
4
5use parking_lot::Mutex;
6use proc_macro2::TokenStream;
7use quote::format_ident;
8
9use crate::config::GeneratorFlags;
10use crate::models::code::ModuleIdent;
11use crate::models::{
12    code::{Module, ModulePath},
13    data::{DataType, PathData},
14    Ident,
15};
16
17use super::MetaData;
18
19/// Context for the rendering process.
20///
21/// This contains different additional information and configuration that may be
22/// needed by a [`Renderer`](super::Renderer) to render the actual code. It is
23/// also used to collect the rendered code and add it to the corresponding module.
24#[derive(Debug)]
25pub struct Context<'a, 'types> {
26    /// Meta information for the rendering process.
27    pub meta: &'a MetaData<'types>,
28
29    /// Data type that needs to be rendered.
30    pub data: &'a DataType<'types>,
31
32    /// Identifier of the data type that needs to be rendered.
33    pub ident: &'a Ident,
34
35    module: Mutex<&'a mut Module>,
36
37    module_path: ModulePath,
38    serialize_module_path: ModulePath,
39    deserialize_module_path: ModulePath,
40
41    values: HashMap<TypeId, Box<dyn Any>>,
42}
43
44pub trait ValueKey: Any {
45    type Type: Any + Clone;
46}
47
48impl<'a, 'types> Context<'a, 'types> {
49    /// Resolves the passed `ident` relative to the module of the current rendered type.
50    pub fn resolve_type_for_module(&self, target_type: &PathData) -> TokenStream {
51        self.add_usings(&target_type.usings);
52
53        target_type.resolve_relative_to(&self.module_path)
54    }
55
56    /// Add using directives to the module the of the current rendered type.
57    pub fn add_usings<I>(&self, usings: I)
58    where
59        I: IntoIterator,
60        I::Item: ToString,
61    {
62        let usings = self.patch_usings(usings);
63        let mut root = self.module.lock();
64        Self::get_current_module(&self.module_path.0, &mut root).usings(usings);
65    }
66
67    /// Add using directives to the root module.
68    pub fn add_root_usings<I>(&self, usings: I)
69    where
70        I: IntoIterator,
71        I::Item: ToString,
72    {
73        let usings = self.patch_usings(usings);
74        self.module.lock().usings(usings);
75    }
76
77    /// Returns a mutable reference to the main module.
78    ///
79    /// This might be useful if you need to add code anywhere to the generated
80    /// result.
81    pub fn main_module(&mut self) -> &mut Module {
82        self.module.get_mut()
83    }
84
85    /// Returns a mutable reference to the module of the current rendered type.
86    pub fn current_module(&mut self) -> &mut Module {
87        let root = self.module.get_mut();
88
89        Self::get_current_module(&self.module_path.0, root)
90    }
91
92    /// Set a `value` for the specified key `K`.
93    pub fn set<K>(&mut self, value: K::Type)
94    where
95        K: ValueKey,
96    {
97        self.values.insert(TypeId::of::<K>(), Box::new(value));
98    }
99
100    /// Get the value that was stored for the specified key `K`.
101    ///
102    /// Panics if the key was not set before.
103    pub fn get<K>(&self) -> K::Type
104    where
105        K: ValueKey,
106    {
107        self.get_ref::<K>().clone()
108    }
109
110    /// Get a reference to the value that was stored for the specified key `K`.
111    ///
112    /// Panics if the key was not set before.
113    pub fn get_ref<K>(&self) -> &K::Type
114    where
115        K: ValueKey,
116    {
117        self.values
118            .get(&TypeId::of::<K>())
119            .unwrap()
120            .downcast_ref::<K::Type>()
121            .unwrap()
122    }
123
124    /// Get a mutable reference to the value that was stored for the specified key `K`.
125    ///
126    /// Panics if the key was not set before.
127    pub fn get_mut<K>(&mut self) -> &mut K::Type
128    where
129        K: ValueKey,
130    {
131        self.values
132            .get_mut(&TypeId::of::<K>())
133            .unwrap()
134            .downcast_mut::<K::Type>()
135            .unwrap()
136    }
137
138    /// Removes any values for the specified key `K`.
139    pub fn unset<K>(&mut self)
140    where
141        K: ValueKey,
142    {
143        self.values.remove(&TypeId::of::<K>());
144    }
145
146    /// Takes an iterator of usings (anything that implements `ToString`) and
147    /// replaces the `xsd_parser::` in the path with the configured name for
148    /// `xsd-parser`.
149    ///
150    /// See [`Renderer::xsd_parser_crate`](crate::pipeline::Renderer::xsd_parser_crate)
151    /// for details.
152    ///
153    /// This should be used before you add using directives to a module manually.
154    /// Normally you should use [`add_usings`](Self::add_usings) which does the
155    /// patching automatically, but if you want to add code somewhere in the module
156    /// tree, this might be useful.
157    pub fn patch_usings<I>(&self, usings: I) -> impl Iterator<Item = String> + use<'_, I>
158    where
159        I: IntoIterator,
160        I::Item: ToString,
161    {
162        let xsd_parser = &self.xsd_parser_crate;
163
164        usings.into_iter().map(move |s| {
165            let s = s.to_string();
166
167            if let Some(s) = s.strip_prefix("xsd_parser::") {
168                format!("{xsd_parser}::{s}")
169            } else {
170                s
171            }
172        })
173    }
174
175    pub(crate) fn resolve_type_for_serialize_module(&self, target_type: &PathData) -> TokenStream {
176        self.add_quick_xml_serialize_usings(&target_type.usings);
177
178        target_type.resolve_relative_to(&self.serialize_module_path)
179    }
180
181    pub(crate) fn resolve_type_for_deserialize_module(
182        &self,
183        target_type: &PathData,
184    ) -> TokenStream {
185        self.add_quick_xml_deserialize_usings(&target_type.usings);
186
187        target_type.resolve_relative_to(&self.deserialize_module_path)
188    }
189
190    pub(crate) fn quick_xml_serialize(&mut self) -> &mut Module {
191        self.current_module().module_mut("quick_xml_serialize")
192    }
193
194    pub(crate) fn quick_xml_deserialize(&mut self) -> &mut Module {
195        self.current_module().module_mut("quick_xml_deserialize")
196    }
197
198    pub(crate) fn add_quick_xml_serialize_usings<I>(&self, usings: I)
199    where
200        I: IntoIterator,
201        I::Item: ToString,
202    {
203        let usings = self.patch_usings(usings);
204
205        let mut root = self.module.lock();
206        Self::get_current_module(&self.module_path.0, &mut root)
207            .module_mut("quick_xml_serialize")
208            .usings(usings);
209    }
210
211    pub(crate) fn add_quick_xml_deserialize_usings<I>(&self, usings: I)
212    where
213        I: IntoIterator,
214        I::Item: ToString,
215    {
216        let usings = self.patch_usings(usings);
217
218        let mut root = self.module.lock();
219        Self::get_current_module(&self.module_path.0, &mut root)
220            .module_mut("quick_xml_deserialize")
221            .usings(usings);
222    }
223
224    pub(super) fn new(
225        meta: &'a MetaData<'types>,
226        data: &'a DataType<'types>,
227        ident: &'a Ident,
228        module: &'a mut Module,
229    ) -> Self {
230        let module_ident = ModuleIdent::new(
231            meta.types,
232            ident,
233            meta.check_generator_flags(GeneratorFlags::USE_NAMESPACE_MODULES),
234            meta.check_generator_flags(GeneratorFlags::USE_SCHEMA_MODULES),
235        );
236        let module_path = ModulePath::from_ident(meta.types.meta.types, module_ident);
237        let serialize_module_path = module_path
238            .clone()
239            .join(format_ident!("quick_xml_serialize"));
240        let deserialize_module_path = module_path
241            .clone()
242            .join(format_ident!("quick_xml_deserialize"));
243
244        Self {
245            meta,
246            data,
247            ident,
248            module: Mutex::new(module),
249
250            module_path,
251            serialize_module_path,
252            deserialize_module_path,
253
254            values: HashMap::new(),
255        }
256    }
257
258    fn get_current_module<I>(idents: I, root: &mut Module) -> &mut Module
259    where
260        I: IntoIterator,
261        I::Item: ToString,
262    {
263        let mut module = root;
264
265        for ident in idents {
266            module = module.module_mut(ident.to_string());
267        }
268
269        module
270    }
271}
272
273impl<'types> Deref for Context<'_, 'types> {
274    type Target = MetaData<'types>;
275
276    fn deref(&self) -> &Self::Target {
277        self.meta
278    }
279}