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 = ®istry[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}