1use base64::Engine;
2use ploidy_pointer::{JsonPointee, JsonPointeeError, JsonPointer, JsonPointerTypeError};
3use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
4
5#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct Base64(Vec<u8>);
9
10impl Base64 {
11 #[inline]
12 pub fn into_vec(self) -> Vec<u8> {
13 self.0
14 }
15}
16
17impl JsonPointee for Base64 {
18 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
19 if pointer.is_empty() {
20 Ok(self as &dyn JsonPointee)
21 } else {
22 Err(JsonPointerTypeError::new(pointer).into())
23 }
24 }
25}
26
27impl AsRef<[u8]> for Base64 {
28 #[inline]
29 fn as_ref(&self) -> &[u8] {
30 &self.0
31 }
32}
33
34impl AsMut<[u8]> for Base64 {
35 #[inline]
36 fn as_mut(&mut self) -> &mut [u8] {
37 &mut self.0
38 }
39}
40
41impl From<Vec<u8>> for Base64 {
42 #[inline]
43 fn from(value: Vec<u8>) -> Self {
44 Self(value)
45 }
46}
47
48impl From<&[u8]> for Base64 {
49 #[inline]
50 fn from(value: &[u8]) -> Self {
51 Self(value.to_vec())
52 }
53}
54
55impl Serialize for Base64 {
56 #[inline]
57 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
58 let encoded = base64::engine::general_purpose::STANDARD.encode(&self.0);
59 serializer.serialize_str(&encoded)
60 }
61}
62
63impl<'de> Deserialize<'de> for Base64 {
64 #[inline]
65 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
66 use de::Error;
67 let value: &'de str = Deserialize::deserialize(deserializer)?;
68 let decoded = base64::engine::general_purpose::STANDARD
69 .decode(value)
70 .map_err(|err| D::Error::custom(Base64Error(err)))?;
71 Ok(Base64(decoded))
72 }
73}
74
75#[derive(Debug, thiserror::Error)]
76#[error("byte string contains invalid Base64: {0}")]
77pub struct Base64Error(#[from] base64::DecodeError);
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_serialize_empty() {
85 let byte = Base64::default();
86 let json = serde_json::to_string(&byte).unwrap();
87 assert_eq!(json, r#""""#);
88 }
89
90 #[test]
91 fn test_serialize_text_data() {
92 let byte = Base64::from(b"Hello, World!".as_slice());
93 let json = serde_json::to_string(&byte).unwrap();
94 assert_eq!(json, r#""SGVsbG8sIFdvcmxkIQ==""#);
95 }
96
97 #[test]
98 fn test_serialize_binary_data() {
99 let byte = Base64::from(vec![0x00, 0x01, 0x02, 0xff, 0xfe, 0xfd]);
100 let json = serde_json::to_string(&byte).unwrap();
101 assert_eq!(json, r#""AAEC//79""#);
102 }
103
104 #[test]
105 fn test_deserialize_empty() {
106 let byte: Base64 = serde_json::from_str(r#""""#).unwrap();
107 assert_eq!(byte.as_ref(), b"");
108 }
109
110 #[test]
111 fn test_deserialize_text_data() {
112 let byte: Base64 = serde_json::from_str(r#""SGVsbG8sIFdvcmxkIQ==""#).unwrap();
113 assert_eq!(byte.as_ref(), b"Hello, World!");
114 }
115
116 #[test]
117 fn test_deserialize_binary_data() {
118 let byte: Base64 = serde_json::from_str(r#""AAEC//79""#).unwrap();
119 assert_eq!(byte.as_ref(), &[0x00, 0x01, 0x02, 0xff, 0xfe, 0xfd]);
120 }
121
122 #[test]
123 fn test_deserialize_invalid_base64() {
124 let result: Result<Base64, _> = serde_json::from_str(r#""not valid base64!!!""#);
125 assert!(result.is_err());
126 }
127
128 #[test]
129 fn test_roundtrip() {
130 let original = Base64::from(vec![0x00, 0x7f, 0x80, 0xff, 0x42]);
131 let json = serde_json::to_string(&original).unwrap();
132 let restored: Base64 = serde_json::from_str(&json).unwrap();
133 assert_eq!(original, restored);
134 }
135}