From 67e7e9da156613de9895c6dbdb5832cc628610b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sun, 27 Apr 2025 12:53:24 +0200 Subject: [PATCH] xso: improve documentation around text traits for third-party types The previous wording was a bit ominous in places ("Because of the unfortunate situation as described in `FromXmlText`"). This should be clearer and provides hopefully clearer instructions. --- xso/src/lib.rs | 33 +++++++-------------- xso/src/text.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 23 deletions(-) diff --git a/xso/src/lib.rs b/xso/src/lib.rs index dc314884f2f90a04c3928ecbf0898782914fcd5b..3cec6221488c653fc364d9f266ec6890dcac9505 100644 --- a/xso/src/lib.rs +++ b/xso/src/lib.rs @@ -248,18 +248,13 @@ pub trait FromXml { /// Trait allowing to convert XML text to a value. /// -/// This trait is similar to [`core::str::FromStr`], however, due to -/// restrictions imposed by the orphan rule, a separate trait is needed. -/// Implementations for many standard library types are available. In -/// addition, the following feature flags can enable more implementations: +/// This trait is similar to [`FromStr`][`core::str::FromStr`], however, to +/// allow specialisation for XML<->Text conversion, a separate trait is +/// introduced. Unlike `FromStr`, this trait allows taking ownership of the +/// original text data, potentially saving allocations. /// -/// - `jid`: `jid::Jid`, `jid::BareJid`, `jid::FullJid` -/// - `uuid`: `uuid::Uuid` -/// -/// Because of this unfortunate situation, we are **extremely liberal** with -/// accepting optional dependencies for this purpose. You are very welcome to -/// make merge requests against this crate adding support for parsing -/// third-party crates. +/// **Important:** See the [`text`][`crate::text`] module's documentation +/// for notes regarding implementations for types from third-party crates. pub trait FromXmlText: Sized { /// Convert the given XML text to a value. fn from_xml_text(data: String) -> Result; @@ -306,17 +301,8 @@ impl FromXmlText for Box { /// serialisation in XML text, you should *only* implement /// [`AsOptionalXmlText`] and omit the [`AsXmlText`] implementation. /// -/// This trait is implemented for many standard library types implementing -/// [`core::fmt::Display`]. In addition, the following feature flags can enable -/// more implementations: -/// -/// - `jid`: `jid::Jid`, `jid::BareJid`, `jid::FullJid` -/// - `uuid`: `uuid::Uuid` -/// -/// Because of the unfortunate situation as described in [`FromXmlText`], we -/// are **extremely liberal** with accepting optional dependencies for this -/// purpose. You are very welcome to make merge requests against this crate -/// adding support for parsing third-party crates. +/// **Important:** See the [`text`][`crate::text`] module's documentation +/// for notes regarding implementations for types from third-party crates. pub trait AsXmlText { /// Convert the value to an XML string in a context where an absent value /// cannot be represented. @@ -380,6 +366,9 @@ impl AsXmlText for &T { /// If your type can be serialised as both (text and attribute) but needs /// special handling in attributes, implement [`AsXmlText`] but provide a /// custom implementation of [`AsXmlText::as_optional_xml_text`]. +/// +/// **Important:** See the [`text`][`crate::text`] module's documentation +/// for notes regarding implementations for types from third-party crates. pub trait AsOptionalXmlText { /// Convert the value to an XML string in a context where an absent value /// can be represented. diff --git a/xso/src/text.rs b/xso/src/text.rs index a925cc106a70ec3dbcb937d261f6a4aaa085a80b..f44ef4a66650c0dddb87bf46022423e4a9861d55 100644 --- a/xso/src/text.rs +++ b/xso/src/text.rs @@ -4,7 +4,84 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -//! Module containing implementations for conversions to/from XML text. +//! # Convert data to and from XML text +//! +//! This module provides traits and types related to conversion of XML text +//! data to and from Rust types, as well as the [`AsXmlText`], +//! [`AsOptionalXmlText`][`crate::AsOptionalXmlText`] and [`FromXmlText`] +//! implementations for foreign and standard-library types. +//! +//! ## Support for types from third-party crates +//! +//! Beyond the standard library types, the following additional types are +//! supported: +//! +//! | Feature gate | Types | +//! | --- | --- | +//! | `jid` | `jid::Jid`, `jid::BareJid`, `jid::FullJid` | +//! | `serde_json` | `serde_json::Value` | +//! | `uuid` | `uuid::Uuid` | +//! +//! ### Adding support for more types +//! +//! Due to the orphan rule, it is not possible for applications to implement +//! [`AsXmlText`], [`AsOptionalXmlText`][`crate::AsOptionalXmlText`] or +//! [`FromXmlText`] on types which originate from third-party crates. Because +//! of that, we are **extremely liberal** at accepting merge requests for +//! implementations of these traits for types from third-party crates. +//! +//! The only requirement is that the implementation is gated behind a feature +//! flag which is disabled-by-default. +//! +//! ### Workaround for unsupported types +//! +//! If making a merge request against `xso` and waiting for a release is not +//! an option, you can use newtype wrappers in almost all cases, for 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, AsXmlText, FromXmlText, error::Error}; +//! # use std::borrow::Cow; +//! use std::process::ExitCode; +//! +//! struct MyExitCode(ExitCode); +//! +//! impl AsXmlText for MyExitCode { +//! fn as_xml_text(&self) -> Result, Error> { +//! match self.0 { +//! ExitCode::FAILURE => Ok(Cow::Borrowed("failure")), +//! ExitCode::SUCCESS => Ok(Cow::Borrowed("success")), +//! _ => Err(Error::Other("unknown exit code")), +//! } +//! } +//! } +//! +//! impl FromXmlText for MyExitCode { +//! fn from_xml_text(s: String) -> Result { +//! match s.as_str() { +//! "failure" => Ok(Self(ExitCode::FAILURE)), +//! "success" => Ok(Self(ExitCode::SUCCESS)), +//! _ => Err(Error::Other("unknown exit code")), +//! } +//! } +//! } +//! +//! #[derive(AsXml, FromXml)] +//! #[xml(namespace = "urn:example", name = "process-result")] +//! struct ProcessResult { +//! #[xml(attribute)] +//! code: MyExitCode, +//! #[xml(text)] +//! stdout: String, +//! } +//! ``` +//! +//! Of course, such an approach reduces the usability of your struct (and +//! comes with issues once references are needed), so making a merge request +//! against `xso` is generally preferable. use core::marker::PhantomData;