From 69787569c9e9cca24a7721a4a8bd20617bfe49b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sat, 25 Jan 2025 12:22:05 +0100 Subject: [PATCH] xso: implement TextCodec<_> on all T: base64::engine::Engine The `xso::text::Base64` struct remains as a shorthand (because frankly, I find the const names in the base64 crate very unwieldly), but you can now use any of the base64 engines as codec. --- xso/ChangeLog | 2 ++ xso/src/text.rs | 59 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/xso/ChangeLog b/xso/ChangeLog index 4562cef625386aaeab4c55dcd9fae7213dc39050..796bee953c755ad92e3a18f196ed3758d9d5182a 100644 --- a/xso/ChangeLog +++ b/xso/ChangeLog @@ -27,6 +27,8 @@ Version NEXT: jid::DomainPart, and jid::ResourcePart (!485) - Support for optional child elements, the presence of which are translated into a boolean (`#[xml(flag)]`). + - Generic TextCodec implementation for all base64 engines provided by + the base64 crate (if the `base64` feature is enabled). Version 0.1.2: 2024-07-26 Jonas Schäfer diff --git a/xso/src/text.rs b/xso/src/text.rs index 871be16e15e080423ea09946de69c70017be484b..cecbf380067730971b708d3328bb46cf184dce27 100644 --- a/xso/src/text.rs +++ b/xso/src/text.rs @@ -18,7 +18,7 @@ use alloc::{ use crate::{error::Error, AsXmlText, FromXmlText}; #[cfg(feature = "base64")] -use base64::engine::{general_purpose::STANDARD as StandardBase64Engine, Engine as _}; +use base64::engine::general_purpose::STANDARD as StandardBase64Engine; macro_rules! convert_via_fromstr_and_display { ($($(#[cfg $cfg:tt])?$t:ty,)+) => { @@ -291,38 +291,47 @@ impl TextFilter for StripWhitespace { } } -/// Text codec transforming text to binary using standard base64. +/// Text codec transforming text to binary using standard `base64`. /// /// The `Filter` type argument can be used to employ additional preprocessing /// of incoming text data. Most interestingly, passing [`StripWhitespace`] /// will make the implementation ignore any whitespace within the text. +/// +/// `Base64` uses the [`base64::engine::general_purpose::STANDARD`] engine. +/// [`TextCodec`] is also automatically implemented for any value which +/// implements [`base64::engine::Engine`], allowing you to choose different +/// alphabets easily. #[cfg(feature = "base64")] pub struct Base64; #[cfg(feature = "base64")] impl TextCodec> for Base64 { fn decode(&self, s: String) -> Result, Error> { - StandardBase64Engine - .decode(s.as_bytes()) + base64::engine::Engine::decode(&StandardBase64Engine, s.as_bytes()) .map_err(Error::text_parse_error) } fn encode<'x>(&self, value: &'x Vec) -> Result>, Error> { - Ok(Some(Cow::Owned(StandardBase64Engine.encode(&value)))) + Ok(Some(Cow::Owned(base64::engine::Engine::encode( + &StandardBase64Engine, + &value, + )))) } } #[cfg(feature = "base64")] impl<'x> TextCodec> for Base64 { fn decode(&self, s: String) -> Result, Error> { - StandardBase64Engine - .decode(s.as_bytes()) + base64::engine::Engine::decode(&StandardBase64Engine, s.as_bytes()) .map_err(Error::text_parse_error) .map(Cow::Owned) } fn encode<'a>(&self, value: &'a Cow<'x, [u8]>) -> Result>, Error> { - Ok(Some(Cow::Owned(StandardBase64Engine.encode(&value)))) + Ok(Some(Cow::Owned(base64::engine::Engine::encode( + &StandardBase64Engine, + &value, + )))) } } @@ -347,6 +356,40 @@ where } } +#[cfg(feature = "base64")] +impl TextCodec> for T { + fn decode(&self, s: String) -> Result, Error> { + base64::engine::Engine::decode(self, s.as_bytes()).map_err(Error::text_parse_error) + } + + fn encode<'x>(&self, value: &'x Vec) -> Result>, Error> { + Ok(Some(Cow::Owned(base64::engine::Engine::encode( + self, &value, + )))) + } +} + +#[cfg(feature = "base64")] +impl<'a, T: base64::engine::Engine, U> TextCodec> for T +where + T: TextCodec, +{ + fn decode(&self, s: String) -> Result, Error> { + if s.is_empty() { + return Ok(None); + } + Ok(Some(TextCodec::decode(self, s)?)) + } + + fn encode<'x>(&self, decoded: &'x Option) -> Result>, Error> { + decoded + .as_ref() + .map(|x| TextCodec::encode(self, x)) + .transpose() + .map(Option::flatten) + } +} + /// Text codec transforming text to binary using hexadecimal nibbles. /// /// The length must be known at compile-time.