fn encode_hex(bytes: &[u8]) -> String { use std::fmt::Write as _; let mut out = String::with_capacity(bytes.len() * 2); for byte in bytes { let _ = write!(out, "{byte:02x}"); } out } fn decode_hex(text: &str) -> Result, String> { if !text.len().is_multiple_of(2) { return Err("hex string must have an even number of digits".to_string()); } let mut out = Vec::with_capacity(text.len() / 2); let raw = text.as_bytes(); for pair in raw.chunks(2) { let hi = (pair[0] as char) .to_digit(16) .ok_or_else(|| format!("invalid hex digit `{}`", pair[0] as char))?; let lo = (pair[1] as char) .to_digit(16) .ok_or_else(|| format!("invalid hex digit `{}`", pair[1] as char))?; out.push(((hi << 4) | lo) as u8); } Ok(out) } fn timestamp_to_text(value: &prost_types::Timestamp) -> Option { chrono::DateTime::from_timestamp(value.seconds, value.nanos as u32).map(|dt| dt.to_rfc3339()) } fn timestamp_from_text(text: &str) -> Result { let parsed = chrono::DateTime::parse_from_rfc3339(text).map_err(|err| err.to_string())?; let utc = parsed.with_timezone(&chrono::Utc); Ok(prost_types::Timestamp { seconds: utc.timestamp(), nanos: utc.timestamp_subsec_nanos() as i32, }) } pub mod bytes { use serde::{Deserialize, Deserializer, Serializer}; pub fn serialize(value: &[u8], serializer: S) -> Result { serializer.serialize_str(&super::encode_hex(value)) } pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { let text = String::deserialize(deserializer)?; super::decode_hex(&text).map_err(serde::de::Error::custom) } } pub mod bytes_list { use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub fn serialize(value: &[Vec], serializer: S) -> Result { let texts: Vec = value.iter().map(|item| super::encode_hex(item)).collect(); texts.serialize(serializer) } pub fn deserialize<'de, D: Deserializer<'de>>( deserializer: D, ) -> Result>, D::Error> { let texts = Vec::::deserialize(deserializer)?; texts .into_iter() .map(|text| super::decode_hex(&text).map_err(serde::de::Error::custom)) .collect() } } pub mod timestamp_opt { use serde::{Deserialize, Deserializer, Serializer}; pub fn serialize( value: &Option, serializer: S, ) -> Result { match value.as_ref().and_then(super::timestamp_to_text) { Some(text) => serializer.serialize_some(&text), None => serializer.serialize_none(), } } pub fn deserialize<'de, D: Deserializer<'de>>( deserializer: D, ) -> Result, D::Error> { let text = Option::::deserialize(deserializer)?; match text { Some(text) => super::timestamp_from_text(&text) .map(Some) .map_err(serde::de::Error::custom), None => Ok(None), } } }