|
| 1 | +use std::fmt; |
| 2 | +use std::marker::PhantomData; |
| 3 | + |
| 4 | +use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; |
| 5 | +use serde::de::{self, Visitor}; |
| 6 | +use serde::{Deserialize, Deserializer, Serialize, Serializer}; |
| 7 | + |
1 | 8 | pub use crate::LazyLoadBlob; |
2 | 9 |
|
3 | 10 | /// `LazyLoadBlob` is defined in the wit bindings, but constructors and methods here. |
@@ -42,3 +49,94 @@ impl std::cmp::PartialEq for LazyLoadBlob { |
42 | 49 | self.mime == other.mime && self.bytes == other.bytes |
43 | 50 | } |
44 | 51 | } |
| 52 | + |
| 53 | +impl Serialize for LazyLoadBlob { |
| 54 | + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 55 | + where |
| 56 | + S: Serializer, |
| 57 | + { |
| 58 | + // Create a struct with 2 fields |
| 59 | + use serde::ser::SerializeStruct; |
| 60 | + let mut state = serializer.serialize_struct("LazyLoadBlob", 2)?; |
| 61 | + |
| 62 | + // Serialize mime normally (serde handles Option automatically) |
| 63 | + state.serialize_field("mime", &self.mime)?; |
| 64 | + |
| 65 | + let base64_data = BASE64.encode(&self.bytes); |
| 66 | + state.serialize_field("bytes", &base64_data)?; |
| 67 | + |
| 68 | + state.end() |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +// Custom visitor for deserialization |
| 73 | +struct LazyLoadBlobVisitor { |
| 74 | + marker: PhantomData<fn() -> LazyLoadBlob>, |
| 75 | +} |
| 76 | + |
| 77 | +impl LazyLoadBlobVisitor { |
| 78 | + fn new() -> Self { |
| 79 | + LazyLoadBlobVisitor { |
| 80 | + marker: PhantomData, |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +impl<'de> Visitor<'de> for LazyLoadBlobVisitor { |
| 86 | + type Value = LazyLoadBlob; |
| 87 | + |
| 88 | + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| 89 | + formatter.write_str("a struct with mime and bytes fields") |
| 90 | + } |
| 91 | + |
| 92 | + fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error> |
| 93 | + where |
| 94 | + M: de::MapAccess<'de>, |
| 95 | + { |
| 96 | + let mut mime = None; |
| 97 | + let mut bytes_base64 = None; |
| 98 | + |
| 99 | + // Extract each field from the map |
| 100 | + while let Some(key) = map.next_key::<String>()? { |
| 101 | + match key.as_str() { |
| 102 | + "mime" => { |
| 103 | + if mime.is_some() { |
| 104 | + return Err(de::Error::duplicate_field("mime")); |
| 105 | + } |
| 106 | + mime = map.next_value()?; |
| 107 | + } |
| 108 | + "bytes" => { |
| 109 | + if bytes_base64.is_some() { |
| 110 | + return Err(de::Error::duplicate_field("bytes")); |
| 111 | + } |
| 112 | + bytes_base64 = Some(map.next_value::<String>()?); |
| 113 | + } |
| 114 | + _ => { |
| 115 | + // Skip unknown fields |
| 116 | + let _ = map.next_value::<de::IgnoredAny>()?; |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + let bytes_base64 = bytes_base64.ok_or_else(|| de::Error::missing_field("bytes"))?; |
| 122 | + |
| 123 | + let bytes = BASE64 |
| 124 | + .decode(bytes_base64.as_bytes()) |
| 125 | + .map_err(|err| de::Error::custom(format!("Invalid base64: {}", err)))?; |
| 126 | + |
| 127 | + Ok(LazyLoadBlob { mime, bytes }) |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +impl<'de> Deserialize<'de> for LazyLoadBlob { |
| 132 | + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 133 | + where |
| 134 | + D: Deserializer<'de>, |
| 135 | + { |
| 136 | + deserializer.deserialize_struct( |
| 137 | + "LazyLoadBlob", |
| 138 | + &["mime", "bytes"], |
| 139 | + LazyLoadBlobVisitor::new(), |
| 140 | + ) |
| 141 | + } |
| 142 | +} |
0 commit comments