Skip to main content

oxixml_xml/de/
resolver.rs

1//! Entity resolver module
2
3use std::convert::Infallible;
4use std::error::Error;
5
6use crate::escape::resolve_predefined_entity;
7use crate::events::BytesText;
8
9/// Used to resolve unknown entities while parsing
10///
11/// # Example
12///
13/// ```
14/// # use serde::Deserialize;
15/// use std::collections::BTreeMap;
16/// use std::string::FromUtf8Error;
17/// use oxixml_xml::de::{Deserializer, EntityResolver};
18/// use oxixml_xml::events::BytesText;
19///
20/// #[derive(Default)]
21/// struct DocTypeEntityResolver {
22///     map: BTreeMap<String, String>,
23/// }
24///
25/// impl EntityResolver for DocTypeEntityResolver {
26///     type Error = FromUtf8Error;
27///
28///     fn capture(&mut self, doctype: BytesText) -> Result<(), Self::Error> {
29///         // We do not focus on true parsing in this example: this only handles
30///         // `<!ENTITY name "value">` declarations. You should use a dedicated
31///         // DTD parser in real code.
32///         let text = String::from_utf8(doctype.to_vec())?;
33///         let mut rest = text.as_str();
34///         while let Some(start) = rest.find("<!ENTITY") {
35///             rest = rest[start + "<!ENTITY".len()..].trim_start();
36///             let name_len = rest.find(char::is_whitespace).unwrap_or(rest.len());
37///             let name = rest[..name_len].to_string();
38///             rest = &rest[name_len..];
39///
40///             let Some(open) = rest.find('"') else { break };
41///             rest = &rest[open + 1..];
42///             let Some(close) = rest.find('"') else { break };
43///             self.map.insert(name, rest[..close].to_string());
44///             rest = &rest[close + 1..];
45///         }
46///         Ok(())
47///     }
48///
49///     fn resolve(&self, entity: &str) -> Option<&str> {
50///         self.map.get(entity).map(|s| s.as_str())
51///     }
52/// }
53///
54/// let xml_reader = br#"
55///     <!DOCTYPE dict[ <!ENTITY e1 "entity 1"> ]>
56///     <root>
57///         <entity_one>&e1;</entity_one>
58///     </root>
59/// "#.as_ref();
60///
61/// let mut de = Deserializer::with_resolver(
62///     xml_reader,
63///     DocTypeEntityResolver::default(),
64/// );
65/// let data: BTreeMap<String, String> = BTreeMap::deserialize(&mut de).expect("should be Ok");
66///
67/// assert_eq!(data.get("entity_one"), Some(&"entity 1".to_string()));
68/// ```
69pub trait EntityResolver {
70    /// The error type that represents DTD parse error
71    type Error: Error;
72
73    /// Called on contents of [`Event::DocType`] to capture declared entities.
74    /// Can be called multiple times, for each parsed `<!DOCTYPE >` declaration.
75    ///
76    /// [`Event::DocType`]: crate::events::Event::DocType
77    fn capture(&mut self, doctype: BytesText) -> Result<(), Self::Error>;
78
79    /// Called when an entity needs to be resolved.
80    ///
81    /// `None` is returned if a suitable value can not be found.
82    /// In that case an [`EscapeError::UnrecognizedEntity`] will be returned by
83    /// a deserializer.
84    ///
85    /// [`EscapeError::UnrecognizedEntity`]: crate::escape::EscapeError::UnrecognizedEntity
86    fn resolve(&self, entity: &str) -> Option<&str>;
87}
88
89/// An [`EntityResolver`] that resolves only predefined entities:
90///
91/// | Entity | Resolution
92/// |--------|------------
93/// |`&lt;`  | `<`
94/// |`&gt;`  | `>`
95/// |`&amp;` | `&`
96/// |`&apos;`| `'`
97/// |`&quot;`| `"`
98#[derive(Default, Copy, Clone)]
99pub struct PredefinedEntityResolver;
100
101impl EntityResolver for PredefinedEntityResolver {
102    type Error = Infallible;
103
104    #[inline]
105    fn capture(&mut self, _doctype: BytesText) -> Result<(), Self::Error> {
106        Ok(())
107    }
108
109    #[inline]
110    fn resolve(&self, entity: &str) -> Option<&str> {
111        resolve_predefined_entity(entity)
112    }
113}