Skip to main content

oxixml_xml/
lib.rs

1//! `oxixml-xml` — XML 1.0/1.1 pull parser and writer, quick-xml 0.41-compatible.
2//!
3//! Part of the [OxiXML](https://github.com/cool-japan/oxixml) workspace — the
4//! COOLJAPAN Pure-Rust foundation for all XML and RDF processing. This crate is a
5//! drop-in replacement for [`quick-xml`](https://docs.rs/quick-xml): it mirrors
6//! the 0.41 public API (module paths, type names and method signatures) so that
7//! downstream projects can switch over with a one-line dependency change.
8//!
9//! It provides the zero-copy event model, its supporting vocabulary, and the
10//! pull-based [`Reader`]:
11//!
12//! * [`events`] — the [`Event`](events::Event) enum and its `Bytes*` payloads
13//!   ([`BytesStart`](events::BytesStart), [`BytesText`](events::BytesText), …),
14//!   plus the [`attributes`](events::attributes) iterator.
15//! * [`name`] — [`QName`](name::QName), [`LocalName`](name::LocalName),
16//!   [`Prefix`](name::Prefix), [`Namespace`](name::Namespace) and the
17//!   [`NamespaceResolver`](name::NamespaceResolver).
18//! * [`escape`] — [`escape`](escape::escape) / [`unescape`](escape::unescape) and
19//!   friends, including attribute-value normalization.
20//! * [`encoding`] — the [`Decoder`] (UTF-8 always; non-UTF-8 behind the
21//!   `encoding` feature, backed by `oxixml-encoding`).
22//! * [`errors`] — [`Error`], [`Result`], [`SyntaxError`](errors::SyntaxError) and
23//!   [`IllFormedError`](errors::IllFormedError).
24//! * [`reader`] — the pull-based [`Reader`] and its
25//!   [`Config`](reader::Config), plus the namespace-aware
26//!   [`NsReader`] that resolves prefixes to namespaces.
27//! * [`writer`] — the events-based [`Writer`] and its
28//!   [`ElementWriter`] builder.
29//! * [`parser`] — the low-level incremental parsers that the [`Reader`] drives
30//!   ([`ElementParser`](parser::ElementParser), [`CommentParser`](parser::CommentParser),
31//!   [`PiParser`](parser::PiParser)).
32//!
33//! # Conformance
34//!
35//! Parsing is `quick-xml`-compatible — that is, deliberately permissive — by
36//! default. Setting [`Config::check_well_formedness`](reader::Config::check_well_formedness)
37//! (or [`Config::enable_all_checks`](reader::Config::enable_all_checks)) turns the
38//! reader into a conforming **non-validating** XML processor: the `Char` and
39//! `Name` productions, the document structure, the grammar of the XML declaration
40//! and of the internal DTD subset, and the entity-related well-formedness
41//! constraints are then all enforced, and violations surface as
42//! [`Error::NotWellFormed`]. Driven through an
43//! [`NsReader`], the extra `QName` and prefix-binding
44//! constraints of Namespaces in XML are enforced as well.
45//!
46//! ```
47//! use oxixml_xml::reader::Reader;
48//!
49//! let mut reader = Reader::from_str("<a/><b/>");
50//! reader.config_mut().check_well_formedness = true;
51//! let _ = reader.read_event().expect("first root element");
52//! assert!(reader.read_event().is_err(), "a document has a single root element");
53//! ```
54//!
55//! With the `serialize` feature enabled, the crate additionally provides the
56#![cfg_attr(
57    feature = "serialize",
58    doc = "serde deserializer in [`de`] ([`de::from_str`], [`de::from_reader`] and \
59[`de::Deserializer`]) and the matching serializer in [`se`] ([`se::to_string`], \
60[`se::to_string_with_root`], [`se::to_writer`] and [`se::Serializer`]),"
61)]
62#![cfg_attr(
63    not(feature = "serialize"),
64    doc = "serde deserializer in `de` (`from_str`, `from_reader` and `Deserializer`) \
65and the matching serializer in `se` (`to_string`, `to_string_with_root`, `to_writer` \
66and `Serializer`),"
67)]
68//! which mirror the quick-xml 0.41 mapping rules
69//! (`@field` for attributes, `$text` and `$value` special names, sequences of
70//! consecutive elements, and so on).
71//!
72//! # Example
73//!
74//! ```
75//! use oxixml_xml::escape::{escape, unescape};
76//! use oxixml_xml::events::{BytesStart, Event};
77//! use oxixml_xml::name::QName;
78//!
79//! // Build a start tag and inspect its name and attributes.
80//! let start = BytesStart::new("greeting").with_attributes([("lang", "en")]);
81//! assert_eq!(start.name(), QName(b"greeting"));
82//! let (key, value) = {
83//!     let attr = start.attributes().next().expect("one attribute").expect("valid");
84//!     (attr.key.as_ref().to_vec(), attr.value.to_vec())
85//! };
86//! assert_eq!((key.as_slice(), value.as_slice()), (&b"lang"[..], &b"en"[..]));
87//!
88//! // Wrap it in an event; events borrow their payload bytes.
89//! let event = Event::Start(start);
90//! assert_eq!(&*event, b"greeting lang=\"en\"".as_ref());
91//!
92//! // Escape and unescape round-trip.
93//! assert_eq!(escape("a < b & c"), "a &lt; b &amp; c");
94//! assert_eq!(unescape("a &lt; b &amp; c").expect("valid"), "a < b & c");
95//! ```
96
97#![cfg_attr(docsrs, feature(doc_cfg))]
98#![deny(missing_docs)]
99
100#[cfg(feature = "serialize")]
101#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
102pub mod de;
103pub mod encoding;
104pub mod errors;
105pub mod escape;
106pub mod events;
107pub mod name;
108pub mod parser;
109pub mod reader;
110#[cfg(feature = "serialize")]
111#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
112pub mod se;
113#[cfg(feature = "serde-types")]
114#[cfg_attr(docsrs, doc(cfg(feature = "serde-types")))]
115pub mod serde_helpers;
116/// Not an official API; exposed for integration tests.
117#[doc(hidden)]
118pub mod utils;
119mod wellformed;
120pub mod writer;
121
122use std::borrow::Cow;
123
124// Re-exports mirroring quick-xml 0.41.
125pub use crate::encoding::Decoder;
126#[cfg(feature = "serialize")]
127#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
128pub use crate::errors::serialize::{DeError, SeError};
129pub use crate::errors::{Error, Result};
130pub use crate::reader::{NsReader, Reader};
131pub use crate::writer::{ElementWriter, Writer};
132
133/// Version of the XML standard.
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
135pub enum XmlVersion {
136    /// No XML declaration was present, so [version 1.0] is assumed (the default).
137    ///
138    /// [version 1.0]: https://www.w3.org/TR/xml/
139    Implicit1_0,
140    /// The XML version was explicitly declared as [`1.0`](https://www.w3.org/TR/xml/).
141    Explicit1_0,
142    /// The XML version was explicitly declared as [`1.1`](https://www.w3.org/TR/xml11/).
143    Explicit1_1,
144}
145
146impl XmlVersion {
147    /// Normalizes an attribute value according to the rules of this XML version.
148    pub(crate) fn normalize_attribute_value<'input, 'entity, F>(
149        &self,
150        value: &'input str,
151        depth: usize,
152        resolve_entity: F,
153    ) -> std::result::Result<Cow<'input, str>, escape::EscapeError>
154    where
155        // The lifetime of the output comes from a capture or is `'static`.
156        F: FnMut(&str) -> Option<&'entity str>,
157    {
158        match self {
159            Self::Explicit1_1 => {
160                escape::normalize_xml11_attribute_value(value, depth, resolve_entity)
161            }
162            _ => escape::normalize_xml10_attribute_value(value, depth, resolve_entity),
163        }
164    }
165}
166
167impl Default for XmlVersion {
168    #[inline]
169    fn default() -> Self {
170        Self::Implicit1_0
171    }
172}