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 a post-deserialization callback function call. (!553)
|
||||||
- Support for correctly inheriting `xml:lang` values throughout the
|
- Support for correctly inheriting `xml:lang` values throughout the
|
||||||
XML document.
|
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
|
* Changes
|
||||||
- Generated AsXml iterator and FromXml builder types are now
|
- Generated AsXml iterator and FromXml builder types are now
|
||||||
doc(hidden), to not clutter hand-written documentation with auto
|
doc(hidden), to not clutter hand-written documentation with auto
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,10 @@ mod rxml_util;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "macros")]
|
|
||||||
pub mod exports {
|
pub mod exports {
|
||||||
#[cfg(feature = "minidom")]
|
#[cfg(all(feature = "minidom", feature = "macros"))]
|
||||||
pub use minidom;
|
pub use minidom;
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
pub use rxml;
|
pub use rxml;
|
||||||
|
|
||||||
// These re-exports are necessary to support both std and no_std in code
|
// 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
|
/// 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.
|
/// people not having done `type bool = str` or some similar shenanigans.
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
pub type CoreBool = bool;
|
pub type CoreBool = bool;
|
||||||
|
|
||||||
/// The built-in `u8` type.
|
/// The built-in `u8` type.
|
||||||
///
|
///
|
||||||
/// This is re-exported for use by macros in cases where we cannot rely on
|
/// 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.
|
/// people not having done `type u8 = str` or some similar shenanigans.
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
pub type CoreU8 = u8;
|
pub type CoreU8 = u8;
|
||||||
|
|
||||||
/// Compile-time comparison of two strings.
|
/// Compile-time comparison of two strings.
|
||||||
|
|
@ -78,6 +80,7 @@ pub mod exports {
|
||||||
/// Used by macro-generated code.
|
/// Used by macro-generated code.
|
||||||
///
|
///
|
||||||
/// This is necessary because `<str as PartialEq>::eq` is not `const`.
|
/// 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 {
|
pub const fn const_str_eq(a: &'static str, b: &'static str) -> bool {
|
||||||
let a = a.as_bytes();
|
let a = a.as_bytes();
|
||||||
let b = b.as_bytes();
|
let b = b.as_bytes();
|
||||||
|
|
|
||||||
|
|
@ -97,26 +97,67 @@ use crate::{error::Error, AsXmlText, FromXmlText};
|
||||||
#[cfg(feature = "base64")]
|
#[cfg(feature = "base64")]
|
||||||
use base64::engine::general_purpose::STANDARD as StandardBase64Engine;
|
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 {
|
macro_rules! convert_via_fromstr_and_display {
|
||||||
($($(#[cfg $cfg:tt])?$t:ty,)+) => {
|
($($(#[cfg $cfg:tt])?$t:ty),+ $(,)?) => {
|
||||||
$(
|
$(
|
||||||
$(
|
$(
|
||||||
#[cfg $cfg]
|
#[cfg $cfg]
|
||||||
)?
|
)?
|
||||||
impl FromXmlText for $t {
|
impl $crate::FromXmlText for $t {
|
||||||
#[doc = concat!("Parse [`", stringify!($t), "`] from XML text via [`FromStr`][`core::str::FromStr`].")]
|
#[doc = concat!("Parse [`", stringify!($t), "`] from XML text via [`FromStr`][`core::str::FromStr`].")]
|
||||||
fn from_xml_text(s: String) -> Result<Self, Error> {
|
fn from_xml_text(s: String) -> Result<Self, $crate::error::Error> {
|
||||||
s.parse().map_err(Error::text_parse_error)
|
s.parse().map_err($crate::error::Error::text_parse_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
#[cfg $cfg]
|
#[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.")]
|
#[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> {
|
fn as_xml_text(&self) -> Result<$crate::exports::alloc::borrow::Cow<'_, str>, $crate::error::Error> {
|
||||||
Ok(Cow::Owned(self.to_string()))
|
Ok($crate::exports::alloc::borrow::Cow::Owned(self.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue