diff --git a/xso/src/text.rs b/xso/src/text.rs index ad558bad..0f4e90c0 100644 --- a/xso/src/text.rs +++ b/xso/src/text.rs @@ -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 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` on `#[xml(text)]`, which does not support +/// `Option<_>` otherwise. pub struct EmptyAsNone; -impl TextCodec> for EmptyAsNone { - fn decode(&self, s: String) -> Result, Error> { +impl TextCodec> for EmptyAsNone +where + T: FromXmlText + AsXmlText, +{ + fn decode(&self, s: String) -> Result, 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) -> Result>, 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) -> Result>, Error> { + Ok(value + .as_ref() + .map(AsXmlText::as_xml_text) + .transpose()? + .map(|v| (!v.is_empty()).then_some(v)) + .flatten()) } }