xso: Allow any T: FromXmlText + AsXmlText in EmptyAsNone

This text codec was previously implemented only for Option<String>, this
extends it to all types implementing those two traits, such as numbers
or JIDs.
This commit is contained in:
Emmanuel Gil Peyrot 2024-08-03 13:35:41 +02:00
parent 2b346c4e87
commit 7f5b6fec7f

View file

@ -123,7 +123,7 @@ convert_via_fromstr_and_display! {
/// Represent a way to encode/decode text data into a Rust type.
///
/// This trait can be used in scenarios where implementing [`FromXmlText`]
/// This trait can be used in scenarios where implementing [`FromXmlText`]
/// and/or [`AsXmlText`] on a type is not feasible or sensible, such as the
/// following:
///
@ -202,23 +202,32 @@ impl TextCodec<String> for Plain {
}
}
/// Text codec which returns None instead of the empty string.
/// Text codec which returns `None` if the input to decode is the empty string, instead of
/// attempting to decode it.
///
/// Particularly useful when parsing `Option<T>` on `#[xml(text)]`, which does not support
/// `Option<_>` otherwise.
pub struct EmptyAsNone;
impl TextCodec<Option<String>> for EmptyAsNone {
fn decode(&self, s: String) -> Result<Option<String>, Error> {
impl<T> TextCodec<Option<T>> for EmptyAsNone
where
T: FromXmlText + AsXmlText,
{
fn decode(&self, s: String) -> Result<Option<T>, Error> {
if s.is_empty() {
Ok(None)
} else {
Ok(Some(s))
Some(T::from_xml_text(s)).transpose()
}
}
fn encode<'x>(&self, value: &'x Option<String>) -> Result<Option<Cow<'x, str>>, Error> {
Ok(match value.as_ref() {
Some(v) if !v.is_empty() => Some(Cow::Borrowed(v.as_str())),
Some(_) | None => None,
})
fn encode<'x>(&self, value: &'x Option<T>) -> Result<Option<Cow<'x, str>>, Error> {
Ok(value
.as_ref()
.map(AsXmlText::as_xml_text)
.transpose()?
.map(|v| (!v.is_empty()).then_some(v))
.flatten())
}
}