defuse_serde_utils/
base58.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
2use serde_with::{DeserializeAs, SerializeAs};
3
4pub struct Base58;
5
6impl<T> SerializeAs<T> for Base58
7where
8    T: AsRef<[u8]>,
9{
10    fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
11    where
12        S: Serializer,
13    {
14        bs58::encode(source).into_string().serialize(serializer)
15    }
16}
17
18impl<'de, T> DeserializeAs<'de, T> for Base58
19where
20    T: TryFrom<Vec<u8>>,
21{
22    fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
23    where
24        D: Deserializer<'de>,
25    {
26        let s = <&str as Deserialize>::deserialize(deserializer)?;
27
28        let bytes = bs58::decode(s).into_vec().map_err(de::Error::custom)?;
29
30        let length = bytes.len();
31        bytes.try_into().map_err(|_| {
32            de::Error::custom(format_args!(
33                "can't convert a byte vector of length {length} into the output type"
34            ))
35        })
36    }
37}
38
39#[cfg(feature = "abi")]
40const _: () = {
41    use schemars::{
42        JsonSchema,
43        r#gen::SchemaGenerator,
44        schema::{InstanceType, Schema, SchemaObject},
45    };
46    use serde_with::schemars_0_8::JsonSchemaAs;
47
48    impl<T> JsonSchemaAs<T> for Base58 {
49        fn schema_name() -> String {
50            String::schema_name()
51        }
52
53        fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
54            // TODO: use #[schemars(extend(...))] when released
55            SchemaObject {
56                instance_type: Some(InstanceType::String.into()),
57                extensions: std::iter::once(("contentEncoding", "base58".into()))
58                    .map(|(k, v)| (k.to_string(), v))
59                    .collect(),
60                ..Default::default()
61            }
62            .into()
63        }
64
65        fn is_referenceable() -> bool {
66            false
67        }
68    }
69};