From 341d49fecfdd38401106ebd97addb4152ce528ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sun, 27 Apr 2025 13:41:58 +0200 Subject: [PATCH] xso: extend the documentation overall Sprinkling it with examples, adding more words to make things (hopefully) clearer, using a similar structure for different items etc. etc. --- xso/src/lib.rs | 164 ++++++++++++++++++++++++++++++++++++++++--- xso/src/rxml_util.rs | 8 ++- 2 files changed, 159 insertions(+), 13 deletions(-) diff --git a/xso/src/lib.rs b/xso/src/lib.rs index 6b4ab56b8ef1dcdcf2e655400d63bcca8556fd79..215ace3b841bbdde4e1caf1f2eed19efdc7d4daf 100644 --- a/xso/src/lib.rs +++ b/xso/src/lib.rs @@ -342,6 +342,13 @@ impl AsXmlText for str { } } +impl AsXmlText for &str { + /// Return the borrowed string contents. + fn as_xml_text(&self) -> Result, self::error::Error> { + Ok(Cow::Borrowed(self)) + } +} + impl AsXmlText for Box { /// Return the borrowed [`Box`] contents. fn as_xml_text(&self) -> Result, self::error::Error> { @@ -398,7 +405,7 @@ impl AsOptionalXmlText for Option { } } -/// Control how unknown attributes are handled. +/// # Control how unknown attributes are handled /// /// The variants of this enum are referenced in the /// `#[xml(on_unknown_attribute = ..)]` which can be used on structs and @@ -435,7 +442,7 @@ impl UnknownAttributePolicy { } } -/// Control how unknown children are handled. +/// # Control how unknown child elements are handled /// /// The variants of this enum are referenced in the /// `#[xml(on_unknown_child = ..)]` which can be used on structs and @@ -472,8 +479,44 @@ impl UnknownChildPolicy { } } -/// Attempt to transform a type implementing [`AsXml`] into another -/// type which implements [`FromXml`]. +/// # Transform a value into another value via XML +/// +/// This function takes `from`, converts it into XML using its [`AsXml`] +/// implementation and builds a `T` from it (without buffering the tree in +/// memory). +/// +/// If conversion fails, a [`Error`][`crate::error::Error`] is returned. In +/// particular, if `T` expects a different element header than the header +/// provided by `from`, +/// [`Error::TypeMismatch`][`crate::error::Error::TypeMismatch`] is returned. +/// +/// ## Example +/// +#[cfg_attr( + not(all(feature = "std", feature = "macros")), + doc = "Because the std or macros feature was not enabled at doc build time, the example cannot be tested.\n\n```ignore\n" +)] +#[cfg_attr(all(feature = "std", feature = "macros"), doc = "\n```\n")] +/// # use xso::{AsXml, FromXml, transform}; +/// #[derive(AsXml)] +/// #[xml(namespace = "urn:example", name = "foo")] +/// struct Source { +/// #[xml(attribute = "xml:lang")] +/// lang: &'static str, +/// } +/// +/// #[derive(FromXml, PartialEq, Debug)] +/// #[xml(namespace = "urn:example", name = "foo")] +/// struct Dest { +/// #[xml(lang)] +/// lang: Option, +/// } +/// +/// assert_eq!( +/// Dest { lang: Some("en".to_owned()) }, +/// transform(&Source { lang: "en" }).unwrap(), +/// ); +/// ``` pub fn transform(from: &F) -> Result { let mut languages = rxml::xml_lang::XmlLangStack::new(); let mut iter = self::rxml_util::ItemToEvent::new(from.as_xml_iter()?); @@ -568,8 +611,37 @@ pub fn try_from_element( unreachable!("minidom::Element did not produce enough events to complete element") } -/// Attempt to parse a type implementing [`FromXml`] from a byte buffer -/// containing XML data. +/// # Parse a value from a byte slice containing XML data +/// +/// This function parses the XML found in `buf`, assuming it contains a +/// complete XML document (with optional XML declaration) and builds a `T` +/// from it (without buffering the tree in memory). +/// +/// If conversion fails, a [`Error`][`crate::error::Error`] is returned. In +/// particular, if `T` expects a different element header than the element +/// header at the root of the document in `bytes`, +/// [`Error::TypeMismatch`][`crate::error::Error::TypeMismatch`] is returned. +/// +/// ## Example +/// +#[cfg_attr( + not(feature = "macros"), + doc = "Because the macros feature was not enabled at doc build time, the example cannot be tested.\n\n```ignore\n" +)] +#[cfg_attr(feature = "macros", doc = "\n```\n")] +/// # use xso::{AsXml, FromXml, from_bytes}; +/// #[derive(FromXml, PartialEq, Debug)] +/// #[xml(namespace = "urn:example", name = "foo")] +/// struct Foo { +/// #[xml(attribute)] +/// a: String, +/// } +/// +/// assert_eq!( +/// Foo { a: "some-value".to_owned() }, +/// from_bytes(b"").unwrap(), +/// ); +/// ``` pub fn from_bytes(mut buf: &[u8]) -> Result { use rxml::{error::EndOrError, Parse}; @@ -649,7 +721,40 @@ fn read_start_event_io( )) } -/// Attempt to parse a type implementing [`FromXml`] from a reader. +/// # Parse a value from a [`io::BufRead`][`std::io::BufRead`] +/// +/// This function parses the XML found in `r`, assuming it contains a +/// complete XML document (with optional XML declaration) and builds a `T` +/// from it (without buffering the tree in memory). +/// +/// If conversion fails, a [`Error`][`crate::error::Error`] is returned. In +/// particular, if `T` expects a different element header than the element +/// header at the root of the document in `r`, +/// [`Error::TypeMismatch`][`crate::error::Error::TypeMismatch`] is returned. +/// +/// ## Example +/// +#[cfg_attr( + not(feature = "macros"), + doc = "Because the macros feature was not enabled at doc build time, the example cannot be tested.\n\n```ignore\n" +)] +#[cfg_attr(feature = "macros", doc = "\n```\n")] +/// # use xso::{AsXml, FromXml, from_reader}; +/// # use std::io::BufReader; +/// #[derive(FromXml, PartialEq, Debug)] +/// #[xml(namespace = "urn:example", name = "foo")] +/// struct Foo { +/// #[xml(attribute)] +/// a: String, +/// } +/// +/// // let file = .. +/// # let file = &mut &b""[..]; +/// assert_eq!( +/// Foo { a: "some-value".to_owned() }, +/// from_reader(BufReader::new(file)).unwrap(), +/// ); +/// ``` #[cfg(feature = "std")] pub fn from_reader(r: R) -> io::Result { let mut reader = rxml::XmlLangTracker::wrap(rxml::Reader::new(r)); @@ -682,7 +787,34 @@ pub fn from_reader(r: R) -> io::Result { )) } -/// Attempt to serialise a type implementing [`AsXml`] to a vector of bytes. +/// # Serialize a value to UTF-8-encoded XML +/// +/// This function takes `xso`, converts it into XML using its [`AsXml`] +/// implementation and serialises the resulting XML events into a `Vec`. +/// +/// If serialisation fails, an error is returned instead. +/// +/// ## Example +/// +#[cfg_attr( + not(feature = "macros"), + doc = "Because the macros feature was not enabled at doc build time, the example cannot be tested.\n\n```ignore\n" +)] +#[cfg_attr(feature = "macros", doc = "\n```\n")] +/// # use xso::{AsXml, FromXml, to_vec}; +/// # use std::io::BufReader; +/// #[derive(AsXml, PartialEq, Debug)] +/// #[xml(namespace = "urn:example", name = "foo")] +/// struct Foo { +/// #[xml(attribute)] +/// a: String, +/// } +/// +/// assert_eq!( +/// b"", +/// &to_vec(&Foo { a: "some-value".to_owned() }).unwrap()[..], +/// ); +/// ``` pub fn to_vec(xso: &T) -> Result, self::error::Error> { let iter = xso.as_xml_iter()?; let mut writer = rxml::writer::Encoder::new(); @@ -694,10 +826,20 @@ pub fn to_vec(xso: &T) -> Result, self::error::Error> { Ok(buf) } -/// Return true if the string contains exclusively XML whitespace. +/// # Test if a string contains exclusively XML whitespace +/// +/// This function returns true if `s` contains only XML whitespace. XML +/// whitespace is defined as U+0020 (space), U+0009 (tab), U+000a (newline) +/// and U+000d (carriage return), so this test is implemented on bytes instead +/// of codepoints for efficiency. +/// +/// # Example /// -/// XML whitespace is defined as U+0020 (space), U+0009 (tab), U+000a -/// (newline) and U+000d (carriage return). +/// ``` +/// # use xso::is_xml_whitespace; +/// assert!(is_xml_whitespace(" \t\r\n ")); +/// assert!(!is_xml_whitespace(" hello ")); +/// ``` pub fn is_xml_whitespace>(s: T) -> bool { s.as_ref() .iter() diff --git a/xso/src/rxml_util.rs b/xso/src/rxml_util.rs index 48f1771dbb86b22272af1487cc5b37e1ba098e34..61a4ddd46ec1e09677a0bba15a7974200678aa4a 100644 --- a/xso/src/rxml_util.rs +++ b/xso/src/rxml_util.rs @@ -10,9 +10,13 @@ use alloc::borrow::{Cow, ToOwned}; use rxml::{parser::EventMetrics, AttrMap, Event, Namespace, NcName, NcNameStr, XmlVersion}; -/// An encodable item. +/// # Serialisable piece of XML /// -/// Unlike [`rxml::Item`], the contents of this item may either be owned or +/// This item represents a piece of an XML document which can be serialised +/// into bytes by converting it to an [`rxml::Item`] and then feeding it to a +/// [`rxml::Encoder`]. +/// +/// Unlike `rxml::Item`, the contents of this item may either be owned or /// borrowed, individually. This enables the use in an [`crate::AsXml`] trait /// even if data needs to be generated during serialisation. #[derive(Debug)]