Skip to main content

xso/dynxso/
stdreg.rs

1use alloc::{boxed::Box, vec::Vec};
2use core::marker::PhantomData;
3use core::{
4    any::{Any, TypeId},
5    fmt,
6};
7use std::sync::Mutex;
8
9use crate::fromxml::XmlNameMatcher;
10use crate::{
11    error::{Error, FromEventsError},
12    Context, FromEventsBuilder, FromXml,
13};
14
15use super::{
16    registry::{DynXsoRegistryAdd, DynXsoRegistryLookup},
17    MayContain,
18};
19
20#[cfg(feature = "std")]
21type BuilderRegistryBuilder<T> = Box<
22    dyn Fn(
23            rxml::QName,
24            rxml::AttrMap,
25            &Context<'_>,
26        ) -> Result<Box<dyn FromEventsBuilder<Output = Box<T>>>, FromEventsError>
27        + Send
28        + Sync
29        + 'static,
30>;
31
32#[cfg(feature = "std")]
33struct BuilderRegistryEntry<T: ?Sized> {
34    matcher: XmlNameMatcher<'static>,
35    ty: TypeId,
36    builder: BuilderRegistryBuilder<T>,
37}
38
39#[cfg(feature = "std")]
40impl<T: ?Sized> fmt::Debug for BuilderRegistryEntry<T> {
41    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42        f.debug_struct("BuilderRegistryEntry")
43            .field("matcher", &self.matcher)
44            .field("ty", &self.ty)
45            .finish_non_exhaustive()
46    }
47}
48
49/// # Registry for type-erased [`FromXml`] builders which construct `T`
50///
51/// For general information on builder registries, see [`registry`][`super::registry`].
52///
53/// ## Performance trade-offs
54///
55/// The implementation of the `BuilderRegistry` is optimized toward specific
56/// usages. Other usages may see negative performance impacts. The following
57/// assumptions are made:
58///
59/// - Types are added only once at startup and a matching
60///   [`reserve`][`Self::reserve`] call is made beforehand.
61/// - There are many different types.
62/// - [`FromXml::XML_NAME_MATCHER`] returns a different value for most of the
63///   types which are added.
64///
65/// The lookup algorithms in particular are geared toward the implications of
66/// the last two assumptions and may be rather inefficient otherwise.
67///
68/// ## Example
69///
70/// This example illustrates the manual usage of the `BuilderRegistry`.
71///
72#[cfg_attr(
73    not(feature = "macros"),
74    doc = "Because the macros feature was not enabled at doc build time, the example cannot be tested.\n\n```ignore\n"
75)]
76#[cfg_attr(feature = "macros", doc = "\n```\n")]
77/// # use xso::{dynxso::{BuilderRegistry, MayContain, registry::{DynXsoRegistryAdd, DynXsoRegistryLookup}}, FromXml, from_bytes, exports::rxml::{Namespace, AttrMap, QName, Event, parser::EventMetrics, xml_ncname}, Context, FromEventsBuilder, error::FromEventsError};
78/// #[derive(FromXml, Debug, PartialEq)]
79/// #[xml(namespace = "urn:example", name = "foo")]
80/// struct Foo;
81///
82/// #[derive(FromXml, Debug, PartialEq)]
83/// #[xml(namespace = "urn:example", name = "bar")]
84/// struct Bar;
85///
86/// #[derive(Debug, PartialEq)]
87/// enum Either {
88///    A(Foo),
89///    B(Bar),
90/// }
91///
92/// // BuilderRegistry::add::<U> requires that Either: MayContain<U>, in order
93/// // to be able to wrap the U::Builder such that it outputs a Xso<Either>
94/// // instead.
95/// impl MayContain<Foo> for Either {
96///     fn upcast_into(other: Foo) -> Box<Self> { Box::new(Self::A(other)) }
97/// }
98///
99/// impl MayContain<Bar> for Either {
100///     fn upcast_into(other: Bar) -> Box<Self> { Box::new(Self::B(other)) }
101/// }
102///
103/// let registry = BuilderRegistry::<Either>::new();
104/// registry.add::<Foo>();
105/// registry.add::<Bar>();
106///
107/// let mut builder = registry.make_builder(
108///     (
109///         Namespace::from_str("urn:example"),
110///         xml_ncname!("foo").to_owned(),   // <- Selects the Foo variant
111///     ),
112///     AttrMap::new(),
113///     &Context::empty(),
114/// ).unwrap();
115/// let x = builder.feed(Event::EndElement(EventMetrics::zero()), &Context::empty()).unwrap().unwrap();
116/// assert_eq!(Either::A(Foo), *x);
117///
118/// let mut builder = registry.make_builder(
119///     (
120///         Namespace::from_str("urn:example"),
121///         xml_ncname!("bar").to_owned(),   // <- Selects the Bar variant
122///     ),
123///     AttrMap::new(),
124///     &Context::empty(),
125/// ).unwrap();
126/// let x = builder.feed(Event::EndElement(EventMetrics::zero()), &Context::empty()).unwrap().unwrap();
127/// assert_eq!(Either::B(Bar), *x);
128/// ```
129///
130/// Implementing `FromXml` on the `Either` type in the above example would
131/// be trivial and look like this:
132///
133#[cfg_attr(
134    not(feature = "macros"),
135    doc = "Because the macros feature was not enabled at doc build time, the example cannot be tested.\n\n```ignore\n"
136)]
137#[cfg_attr(feature = "macros", doc = "\n```\n")]
138/// # use xso::{dynxso::{BuilderRegistry, MayContain, registry::{DynXsoRegistryAdd, DynXsoRegistryLookup}}, FromXml, from_bytes, exports::rxml::{Namespace, AttrMap, QName, Event, parser::EventMetrics}, Context, FromEventsBuilder, error::FromEventsError};
139/// # #[derive(FromXml, Debug, PartialEq)]
140/// # #[xml(namespace = "urn:example", name = "foo")]
141/// # struct Foo;
142/// #
143/// # #[derive(FromXml, Debug, PartialEq)]
144/// # #[xml(namespace = "urn:example", name = "bar")]
145/// # struct Bar;
146/// #
147/// # #[derive(Debug, PartialEq)]
148/// # enum Either {
149/// #    A(Foo),
150/// #    B(Bar),
151/// # }
152/// #
153/// # impl MayContain<Foo> for Either {
154/// #     fn upcast_into(other: Foo) -> Box<Self> { Box::new(Self::A(other)) }
155/// # }
156/// #
157/// # impl MayContain<Bar> for Either {
158/// #     fn upcast_into(other: Bar) -> Box<Self> { Box::new(Self::B(other)) }
159/// # }
160/// #
161/// // ... code from the previous example ...
162/// use xso::dynxso::UnboxBuilder;
163/// // In order to be able to use the registry from the FromXml implementation,
164/// // it must be a static (as opposed to a local variable as in the previous
165/// // example).
166/// static REGISTRY: BuilderRegistry<Either> = BuilderRegistry::new();
167/// REGISTRY.add::<Foo>();
168/// REGISTRY.add::<Bar>();
169///
170/// impl FromXml for Either {
171///     type Builder = UnboxBuilder<Box<dyn FromEventsBuilder<Output = Box<Either>>>>;
172///
173///     fn from_events(name: QName, attrs: AttrMap, ctx: &Context<'_>) -> Result<Self::Builder, FromEventsError> {
174///         REGISTRY.make_builder(name, attrs, ctx).map(UnboxBuilder::wrap)
175///     }
176/// }
177///
178/// assert_eq!(
179///     Either::A(Foo),
180///     from_bytes("<foo xmlns='urn:example'/>".as_bytes()).unwrap(),
181/// );
182///
183/// assert_eq!(
184///     Either::B(Bar),
185///     from_bytes("<bar xmlns='urn:example'/>".as_bytes()).unwrap(),
186/// );
187/// ```
188#[derive(Debug)]
189#[cfg(feature = "std")]
190pub struct BuilderRegistry<T: ?Sized> {
191    inner: Mutex<Vec<BuilderRegistryEntry<T>>>,
192}
193
194#[cfg(feature = "std")]
195impl<T: ?Sized + 'static> Default for BuilderRegistry<T> {
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201#[cfg(feature = "std")]
202impl<T: ?Sized + 'static> BuilderRegistry<T> {
203    /// Create an empty registry.
204    pub const fn new() -> Self {
205        Self {
206            inner: Mutex::new(Vec::new()),
207        }
208    }
209
210    fn insert(
211        &self,
212        matcher: XmlNameMatcher<'static>,
213        type_id: TypeId,
214        builder: BuilderRegistryBuilder<T>,
215    ) {
216        let mut registry = self.inner.lock().unwrap();
217        let start_scan_at = registry.partition_point(|entry| entry.matcher < matcher);
218        let insert_at = 'outer: {
219            let mut i = start_scan_at;
220            while i < registry.len() {
221                let entry = &registry[i];
222                if entry.matcher == matcher {
223                    if entry.ty == type_id {
224                        // Already inserted.
225                        return;
226                    }
227                    // Still entries with the same matcher -> continue.
228                    i += 1;
229                    continue;
230                }
231
232                // Found insertion point;
233                break 'outer i;
234            }
235
236            // The entire (rest of the) registry contains items matching
237            // `matcher`, but none matching the given `type_id` -> insert at
238            // the end.
239            registry.len()
240        };
241
242        registry.insert(
243            insert_at,
244            BuilderRegistryEntry {
245                matcher,
246                ty: type_id,
247                builder,
248            },
249        );
250    }
251
252    /// Reserve space for at least `n` additional types.
253    pub fn reserve(&self, n: usize) {
254        self.inner.lock().unwrap().reserve(n);
255    }
256
257    fn try_build(
258        inner: &mut [BuilderRegistryEntry<T>],
259        mut name: rxml::QName,
260        mut attrs: rxml::AttrMap,
261        ctx: &Context<'_>,
262        matcher_builder: impl for<'x> FnOnce(&'x rxml::QName) -> XmlNameMatcher<'x>,
263    ) -> Result<Box<dyn FromEventsBuilder<Output = Box<T>>>, FromEventsError> {
264        let matcher = matcher_builder(&name);
265        let start_scan_at = inner.partition_point(|entry| entry.matcher < matcher);
266
267        for entry in &inner[start_scan_at..] {
268            if !entry.matcher.matches(&name) {
269                return Err(FromEventsError::Mismatch { name, attrs });
270            }
271
272            match (entry.builder)(name, attrs, ctx) {
273                Ok(v) => return Ok(v),
274                Err(FromEventsError::Invalid(e)) => return Err(FromEventsError::Invalid(e)),
275                Err(FromEventsError::Mismatch {
276                    name: new_name,
277                    attrs: new_attrs,
278                }) => {
279                    name = new_name;
280                    attrs = new_attrs;
281                }
282            }
283        }
284
285        Err(FromEventsError::Mismatch { name, attrs })
286    }
287}
288
289#[cfg(feature = "std")]
290impl<T: ?Sized + 'static> DynXsoRegistryAdd<T> for BuilderRegistry<T> {
291    fn add<U: Any + FromXml>(&self)
292    where
293        T: MayContain<U>,
294    {
295        struct Wrapper<B, X: ?Sized> {
296            inner: B,
297            output: PhantomData<X>,
298        }
299
300        impl<X: ?Sized, O, B: FromEventsBuilder<Output = O>> FromEventsBuilder for Wrapper<B, X>
301        where
302            X: MayContain<O>,
303        {
304            type Output = Box<X>;
305
306            fn feed(
307                &mut self,
308                ev: rxml::Event,
309                ctx: &Context<'_>,
310            ) -> Result<Option<Self::Output>, Error> {
311                self.inner
312                    .feed(ev, ctx)
313                    .map(|x| x.map(|x| <X as MayContain<O>>::upcast_into(x)))
314            }
315        }
316
317        self.insert(
318            U::XML_NAME_MATCHER,
319            TypeId::of::<U>(),
320            Box::new(|name, attrs, ctx| {
321                U::from_events(name, attrs, ctx).map(|builder| {
322                    Box::new(Wrapper {
323                        inner: builder,
324                        output: PhantomData,
325                    }) as Box<dyn FromEventsBuilder<Output = Box<T>>>
326                })
327            }),
328        )
329    }
330}
331
332#[cfg(feature = "std")]
333impl<T: ?Sized + 'static> DynXsoRegistryLookup<T> for BuilderRegistry<T> {
334    fn make_builder(
335        &self,
336        name: rxml::QName,
337        attrs: rxml::AttrMap,
338        ctx: &Context<'_>,
339    ) -> Result<Box<dyn FromEventsBuilder<Output = Box<T>>>, FromEventsError> {
340        let mut inner = self.inner.lock().unwrap();
341        let (name, attrs) = match Self::try_build(&mut inner, name, attrs, ctx, |qname| {
342            XmlNameMatcher::Specific(qname.0.as_str(), qname.1.as_str())
343        }) {
344            Ok(v) => return Ok(v),
345            Err(FromEventsError::Invalid(e)) => return Err(FromEventsError::Invalid(e)),
346            Err(FromEventsError::Mismatch { name, attrs }) => (name, attrs),
347        };
348
349        let (name, attrs) = match Self::try_build(&mut inner, name, attrs, ctx, |qname| {
350            XmlNameMatcher::InNamespace(qname.0.as_str())
351        }) {
352            Ok(v) => return Ok(v),
353            Err(FromEventsError::Invalid(e)) => return Err(FromEventsError::Invalid(e)),
354            Err(FromEventsError::Mismatch { name, attrs }) => (name, attrs),
355        };
356
357        Self::try_build(&mut inner, name, attrs, ctx, |_| XmlNameMatcher::Any)
358    }
359}