xso: make convert_via_fromstr_and_display available
Useful for dependent crates which want an easy way to provide the AsXmlText / FromXmlText traits.
This commit is contained in:
parent
2a940fb5e0
commit
25d89e02ea
3 changed files with 56 additions and 9 deletions
|
|
@ -50,6 +50,9 @@ Version NEXT:
|
|||
- Support for a post-deserialization callback function call. (!553)
|
||||
- Support for correctly inheriting `xml:lang` values throughout the
|
||||
XML document.
|
||||
- `xso::convert_via_fromstr_and_display`, a declarative macro to provide
|
||||
`AsXmlText` and `FromXmlText` implementations based on the standard
|
||||
library's `Display` and `FromStr` traits.
|
||||
* Changes
|
||||
- Generated AsXml iterator and FromXml builder types are now
|
||||
doc(hidden), to not clutter hand-written documentation with auto
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ mod rxml_util;
|
|||
pub mod text;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "macros")]
|
||||
pub mod exports {
|
||||
#[cfg(feature = "minidom")]
|
||||
#[cfg(all(feature = "minidom", feature = "macros"))]
|
||||
pub use minidom;
|
||||
#[cfg(feature = "macros")]
|
||||
pub use rxml;
|
||||
|
||||
// These re-exports are necessary to support both std and no_std in code
|
||||
|
|
@ -65,12 +65,14 @@ pub mod exports {
|
|||
///
|
||||
/// This is re-exported for use by macros in cases where we cannot rely on
|
||||
/// people not having done `type bool = str` or some similar shenanigans.
|
||||
#[cfg(feature = "macros")]
|
||||
pub type CoreBool = bool;
|
||||
|
||||
/// The built-in `u8` type.
|
||||
///
|
||||
/// This is re-exported for use by macros in cases where we cannot rely on
|
||||
/// people not having done `type u8 = str` or some similar shenanigans.
|
||||
#[cfg(feature = "macros")]
|
||||
pub type CoreU8 = u8;
|
||||
|
||||
/// Compile-time comparison of two strings.
|
||||
|
|
@ -78,6 +80,7 @@ pub mod exports {
|
|||
/// Used by macro-generated code.
|
||||
///
|
||||
/// This is necessary because `<str as PartialEq>::eq` is not `const`.
|
||||
#[cfg(feature = "macros")]
|
||||
pub const fn const_str_eq(a: &'static str, b: &'static str) -> bool {
|
||||
let a = a.as_bytes();
|
||||
let b = b.as_bytes();
|
||||
|
|
|
|||
|
|
@ -97,26 +97,67 @@ use crate::{error::Error, AsXmlText, FromXmlText};
|
|||
#[cfg(feature = "base64")]
|
||||
use base64::engine::general_purpose::STANDARD as StandardBase64Engine;
|
||||
|
||||
/// # Generate `AsXmlText` and `FromXmlText` implementations
|
||||
///
|
||||
/// This macro generates an `AsXmlText` implementation which uses
|
||||
/// [`Display`][`core::fmt::Display`] and an `FromXmlText` which uses
|
||||
/// [`FromStr`][`core::str::FromStr`] for the types it is called on.
|
||||
///
|
||||
/// ## Syntax
|
||||
///
|
||||
/// The macro accepts a comma-separated list of types. Optionally, each type
|
||||
/// can be preceded by a `#[cfg(..)]` attribute to make the implementations
|
||||
/// conditional on a feature.
|
||||
///
|
||||
/// ## 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::convert_via_fromstr_and_display;
|
||||
/// # use core::fmt::{self, Display};
|
||||
/// # use core::str::FromStr;
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl FromStr for Foo {
|
||||
/// # type Err = core::convert::Infallible;
|
||||
/// #
|
||||
/// # fn from_str(s: &str) -> Result<Self, Self::Err> { todo!() }
|
||||
/// /* ... */
|
||||
/// }
|
||||
///
|
||||
/// impl Display for Foo {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { todo!() }
|
||||
/// /* ... */
|
||||
/// }
|
||||
///
|
||||
/// convert_via_fromstr_and_display!(
|
||||
/// Foo,
|
||||
/// );
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! convert_via_fromstr_and_display {
|
||||
($($(#[cfg $cfg:tt])?$t:ty,)+) => {
|
||||
($($(#[cfg $cfg:tt])?$t:ty),+ $(,)?) => {
|
||||
$(
|
||||
$(
|
||||
#[cfg $cfg]
|
||||
)?
|
||||
impl FromXmlText for $t {
|
||||
impl $crate::FromXmlText for $t {
|
||||
#[doc = concat!("Parse [`", stringify!($t), "`] from XML text via [`FromStr`][`core::str::FromStr`].")]
|
||||
fn from_xml_text(s: String) -> Result<Self, Error> {
|
||||
s.parse().map_err(Error::text_parse_error)
|
||||
fn from_xml_text(s: String) -> Result<Self, $crate::error::Error> {
|
||||
s.parse().map_err($crate::error::Error::text_parse_error)
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
#[cfg $cfg]
|
||||
)?
|
||||
impl AsXmlText for $t {
|
||||
impl $crate::AsXmlText for $t {
|
||||
#[doc = concat!("Convert [`", stringify!($t), "`] to XML text via [`Display`][`core::fmt::Display`].\n\nThis implementation never fails.")]
|
||||
fn as_xml_text(&self) -> Result<Cow<'_, str>, Error> {
|
||||
Ok(Cow::Owned(self.to_string()))
|
||||
fn as_xml_text(&self) -> Result<$crate::exports::alloc::borrow::Cow<'_, str>, $crate::error::Error> {
|
||||
Ok($crate::exports::alloc::borrow::Cow::Owned(self.to_string()))
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
|
|
|||
Loading…
Reference in a new issue