#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] //! Bounded XML for hostile input. //! //! - [`parse`] and [`Options::parse`] read bytes into an owned tree of [`El`] and [`Node`], with //! limits on input size, nesting, node count and attributes per element ([`Limits`]). //! - [`Options::parse_streaming`] hands repeated elements to a callback, so a large part never //! becomes a whole tree. [`Options::events`] is the pull reader underneath. //! - [`Names`] chooses how names are read: as written, by local name, or rewritten to the //! caller's canonical prefixes by namespace URI. //! - [`esc`], [`esc_attr`], [`El::to_xml`], [`El::to_xml_pretty`] and [`Writer`] write XML. //! //! No document type definition is processed, and no entity is expanded except the five //! predefined ones (`&` `<` `>` `"` `'`) and numeric character references. //! The text the reader produces is therefore never more than three times its input. //! //! ``` //! let root = craft_xml::parse(b"one & two")?; //! assert_eq!(root.attr_u32("x"), Some(1)); //! assert_eq!(root.child("b").map(|b| b.text().into_owned()).as_deref(), Some("one & two")); //! assert_eq!(root.to_xml(), r#"one & two"#); //! # Ok::<(), craft_xml::Error>(()) //! ``` #[cfg(doctest)] #[doc = include_str!("../README.md")] struct ReadmeExamples; mod read; mod tree; pub mod value; mod write; pub use read::{ AttrValueHook, Canonical, Doctype, ElementHook, Event, Events, Limits, Names, NsName, Options, Scope, UnknownEntity, XML_NS, entity, parse, to_utf8, unescape, }; pub use tree::{Descendants, El, Local, Node, Pat}; pub use write::{DECL, DECL_STANDALONE, Tag, Writer, esc, esc_attr, is_name, is_xml_char, push_esc, push_esc_attr}; /// Why a document was not read. #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum Error { /// The input is longer than [`Limits::max_input`]. #[error("the XML is larger than {0} bytes")] TooLarge(usize), /// Elements nest deeper than [`Limits::max_depth`]. #[error("XML elements are nested deeper than {0} levels")] TooDeep(usize), /// The tree would hold more than [`Limits::max_nodes`] nodes. #[error("the XML holds more than {0} nodes")] TooManyNodes(usize), /// The tree would hold more than [`Limits::max_elements`] elements. #[error("the XML holds more than {0} elements")] TooManyElements(usize), /// One start tag has more than [`Limits::max_attrs`] attributes. #[error("an XML element has more than {0} attributes")] TooManyAttrs(usize), /// The tree would hold more than [`Limits::max_attrs_total`] attributes. #[error("the XML holds more than {0} attributes")] TooManyAttrsTotal(usize), /// The document has a document type declaration that [`Options::doctype`] refuses. #[error("the XML has a document type declaration, which is not accepted")] Doctype, /// A reference to an entity that is neither predefined nor numeric, with /// [`UnknownEntity::Error`]. #[error("the entity `&{name};` at byte {at} is not defined")] UnknownEntity { at: usize, name: String }, /// Text that is not UTF-8, in a strict parse. #[error("the XML is not valid UTF-8 at byte {0}")] Encoding(usize), /// Markup that cannot be read. `at` is a byte offset into the UTF-8 text ([`to_utf8`]). #[error("malformed XML at byte {at}: {msg}")] Syntax { at: usize, msg: String }, /// There is no root element. #[error("the XML has no root element")] NoRoot, } impl Error { /// Was a limit exceeded (as opposed to the markup being wrong)? pub fn is_limit(&self) -> bool { matches!( self, Error::TooLarge(_) | Error::TooDeep(_) | Error::TooManyNodes(_) | Error::TooManyElements(_) | Error::TooManyAttrs(_) | Error::TooManyAttrsTotal(_) ) } }