From b7c9d2753ad5630920758c8ba13e9844e2307ab5 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Wed, 16 Apr 2025 16:15:42 -0700 Subject: [PATCH] add (de)serialization methods for blob & SendError --- Cargo.lock | 1 + Cargo.toml | 1 + src/types/lazy_load_blob.rs | 98 +++++++++++++++++++++++++++++++++++++ src/types/send_error.rs | 2 +- 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 995210e..e484ca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1556,6 +1556,7 @@ dependencies = [ "alloy-sol-macro", "alloy-sol-types", "anyhow", + "base64", "bincode", "color-eyre", "http", diff --git a/Cargo.toml b/Cargo.toml index 60794e3..b7901e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ alloy = { version = "0.8.1", features = [ "rpc-types", ] } anyhow = "1.0" +base64 = "0.22.1" bincode = "1.3.3" color-eyre = { version = "0.6", features = ["capture-spantrace"], optional = true } http = "1.0.0" diff --git a/src/types/lazy_load_blob.rs b/src/types/lazy_load_blob.rs index da46763..3aa833d 100644 --- a/src/types/lazy_load_blob.rs +++ b/src/types/lazy_load_blob.rs @@ -1,3 +1,10 @@ +use std::fmt; +use std::marker::PhantomData; + +use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; +use serde::de::{self, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + pub use crate::LazyLoadBlob; /// `LazyLoadBlob` is defined in the wit bindings, but constructors and methods here. @@ -42,3 +49,94 @@ impl std::cmp::PartialEq for LazyLoadBlob { self.mime == other.mime && self.bytes == other.bytes } } + +impl Serialize for LazyLoadBlob { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Create a struct with 2 fields + use serde::ser::SerializeStruct; + let mut state = serializer.serialize_struct("LazyLoadBlob", 2)?; + + // Serialize mime normally (serde handles Option automatically) + state.serialize_field("mime", &self.mime)?; + + let base64_data = BASE64.encode(&self.bytes); + state.serialize_field("bytes", &base64_data)?; + + state.end() + } +} + +// Custom visitor for deserialization +struct LazyLoadBlobVisitor { + marker: PhantomData LazyLoadBlob>, +} + +impl LazyLoadBlobVisitor { + fn new() -> Self { + LazyLoadBlobVisitor { + marker: PhantomData, + } + } +} + +impl<'de> Visitor<'de> for LazyLoadBlobVisitor { + type Value = LazyLoadBlob; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a struct with mime and bytes fields") + } + + fn visit_map(self, mut map: M) -> Result + where + M: de::MapAccess<'de>, + { + let mut mime = None; + let mut bytes_base64 = None; + + // Extract each field from the map + while let Some(key) = map.next_key::()? { + match key.as_str() { + "mime" => { + if mime.is_some() { + return Err(de::Error::duplicate_field("mime")); + } + mime = map.next_value()?; + } + "bytes" => { + if bytes_base64.is_some() { + return Err(de::Error::duplicate_field("bytes")); + } + bytes_base64 = Some(map.next_value::()?); + } + _ => { + // Skip unknown fields + let _ = map.next_value::()?; + } + } + } + + let bytes_base64 = bytes_base64.ok_or_else(|| de::Error::missing_field("bytes"))?; + + let bytes = BASE64 + .decode(bytes_base64.as_bytes()) + .map_err(|err| de::Error::custom(format!("Invalid base64: {}", err)))?; + + Ok(LazyLoadBlob { mime, bytes }) + } +} + +impl<'de> Deserialize<'de> for LazyLoadBlob { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_struct( + "LazyLoadBlob", + &["mime", "bytes"], + LazyLoadBlobVisitor::new(), + ) + } +} diff --git a/src/types/send_error.rs b/src/types/send_error.rs index d1311b8..acf4ee9 100644 --- a/src/types/send_error.rs +++ b/src/types/send_error.rs @@ -1,7 +1,7 @@ use crate::{Address, LazyLoadBlob, Message, _wit_message_to_message}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct SendError { pub kind: SendErrorKind, pub target: Address,