diff --git a/xso/src/text.rs b/xso/src/text.rs index 9ad3a8ea113dfea595652bfd5fdf22e9d39ef087..8d03d403aefd96eefe742678e9a0b7515e8e3bfc 100644 --- a/xso/src/text.rs +++ b/xso/src/text.rs @@ -243,3 +243,52 @@ impl TextCodec>> for Base64 { .map(Option::flatten) } } + +/// Text codec transforming text to binary using hexadecimal nibbles. +/// +/// The length must be known at compile-time. +pub struct FixedHex; + +impl TextCodec<[u8; N]> for FixedHex { + fn decode(s: String) -> Result<[u8; N], Error> { + if s.len() != 2 * N { + return Err(Error::Other("Invalid length")); + } + + let mut bytes = [0u8; N]; + for i in 0..N { + bytes[i] = + u8::from_str_radix(&s[2 * i..2 * i + 2], 16).map_err(Error::text_parse_error)?; + } + + Ok(bytes) + } + + fn encode(value: &[u8; N]) -> Result>, Error> { + let mut bytes = String::with_capacity(N * 2); + for byte in value { + bytes.extend(format!("{:02x}", byte).chars()); + } + Ok(Some(Cow::Owned(bytes))) + } +} + +impl TextCodec> for FixedHex +where + FixedHex: TextCodec, +{ + fn decode(s: String) -> Result, Error> { + if s.is_empty() { + return Ok(None); + } + Ok(Some(Self::decode(s)?)) + } + + fn encode(decoded: &Option) -> Result>, Error> { + decoded + .as_ref() + .map(Self::encode) + .transpose() + .map(Option::flatten) + } +}