xso: improve documentation around text traits for third-party types

Jonas Schäfer created

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.

Change summary

xso/src/lib.rs  | 33 +++++++--------------
xso/src/text.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 23 deletions(-)

Detailed changes

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<Self, self::error::Error>;
@@ -306,17 +301,8 @@ impl<T: FromXmlText> FromXmlText for Box<T> {
 /// 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<T: AsXmlText> 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.

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<Cow<'_, str>, 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<Self, Error> {
+//!         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;