diff --git a/lexica/src/lib.rs b/lexica/src/lib.rs index deb7d670..ddb6ae36 100644 --- a/lexica/src/lib.rs +++ b/lexica/src/lib.rs @@ -32,7 +32,11 @@ impl StrongRef { } /// Blob type for CBOR deserialization (CAR files, native Cid type) -#[derive(Clone, Debug, Deserialize, Serialize)] +/// Now includes lenient deserialization to handle: +/// - Missing size field (defaults to 0) +/// - Multiple CID field formats (ref as Cid, ref as bytes, legacy cid field) +/// - Schema variations in CAR files +#[derive(Clone, Debug, Serialize)] #[serde(tag = "$type")] #[serde(rename = "blob")] #[serde(rename_all = "camelCase")] @@ -44,6 +48,78 @@ pub struct Blob { pub size: i32, } +impl<'de> Deserialize<'de> for Blob { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::{Error, MapAccess, Visitor}; + use std::fmt; + + struct BlobVisitor; + + impl<'de> Visitor<'de> for BlobVisitor { + type Value = Blob; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a blob object with mimeType and ref/cid fields") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut mime_type: Option = None; + let mut cid: Option = None; + let mut size: Option = None; + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "$type" => { + // Skip $type field + let _: serde::de::IgnoredAny = map.next_value()?; + } + "mimeType" => { + mime_type = Some(map.next_value()?); + } + "ref" => { + // Try to deserialize ref field as Cid (native CBOR format) + cid = Some(map.next_value()?); + } + "cid" => { + // Legacy format: direct cid field + if cid.is_none() { + cid = Some(map.next_value()?); + } else { + let _: serde::de::IgnoredAny = map.next_value()?; + } + } + "size" => { + size = Some(map.next_value()?); + } + _ => { + // Ignore unknown fields + let _: serde::de::IgnoredAny = map.next_value()?; + } + } + } + + let mime_type = mime_type.ok_or_else(|| A::Error::missing_field("mimeType"))?; + let cid = cid.ok_or_else(|| A::Error::missing_field("ref or cid"))?; + let size = size.unwrap_or(0); // Default to 0 if missing + + Ok(Blob { + mime_type, + cid, + size, + }) + } + } + + deserializer.deserialize_map(BlobVisitor) + } +} + /// Blob type for JSON deserialization (Slingshot, string CID or {$link: "cid"} format) /// Supports both modern format (ref.$link) and legacy format (cid directly) #[derive(Clone, Debug, Serialize)]