From 153a40ca2bf20f95906ee68134ae9e983d126e80 Mon Sep 17 00:00:00 2001 From: David Estes Date: Sun, 19 May 2024 22:01:34 -0600 Subject: [PATCH] feat: impl pld support --- Cargo.toml | 1 + src/codec.rs | 14 ++++++++++- src/lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efac211..99fc670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ base64-url = { version = "2.0.2" } ipld-core = { version = "0.4" } serde_ipld_dagjson = { version = "0.2", default-features = false, optional = true } serde_ipld_dagcbor = "0.6" +serde_json = "1" serde = "1" serde_derive = "1" thiserror = "1" diff --git a/src/codec.rs b/src/codec.rs index 281585c..437422e 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -91,7 +91,18 @@ impl TryFrom for JsonWebSignature { type Error = Error; fn try_from(value: Encoded) -> Result { - let link = Cid::try_from(value.payload.as_ref().ok_or(Error::NotJws)?.as_slice())?; + let payload = value.payload.as_ref().ok_or(Error::NotJws)?; + + let (link, pld) = match serde_json::from_slice::(payload.as_ref()) { + Ok(json) => { + let res = match crate::JsonPld(json).try_into().map_err(|_| Error::NotJws)? { + Ipld::Map(map) => map, + _ => return Err(Error::NotJws), + }; + (None, Some(res)) + } + Err(_) => (Some(Cid::try_from(payload.as_ref())?), None), + }; Ok(Self { payload: value .payload @@ -104,6 +115,7 @@ impl TryFrom for JsonWebSignature { .map(Signature::from) .collect(), link, + pld, }) } } diff --git a/src/lib.rs b/src/lib.rs index 18e1b71..c27bb8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,6 @@ use std::collections::BTreeMap; use ipld_core::{ cid::Cid, codec::{Codec, Links}, - ipld, ipld::Ipld, }; use serde_derive::Serialize; @@ -152,22 +151,62 @@ impl Codec for DagJsonCodec { #[derive(Clone, Debug, PartialEq, Serialize)] pub struct JsonWebSignature { /// CID link from the payload. - pub link: Cid, - + #[serde(skip_serializing_if = "Option::is_none")] + pub link: Option, /// The payload base64 url encoded. pub payload: String, - + #[serde(skip_serializing_if = "Option::is_none")] + /// The json payload with ipfs:// prefixed strings replaced with CIDs. + pub pld: Option>, /// The set of signatures. pub signatures: Vec, } -impl<'a> From<&'a JsonWebSignature> for Ipld { - fn from(value: &'a JsonWebSignature) -> Self { - ipld!({ - "payload": value.payload.to_owned(), - "signatures": value.signatures.iter().map(Ipld::from).collect::>(), - "link": value.link, - }) +#[derive(Clone, Debug, PartialEq)] +/// Wrapper around a json value to convert ipfs:// prefixed strings to CIDs. +struct JsonPld(serde_json::Value); + +impl TryFrom for Ipld { + type Error = anyhow::Error; + + fn try_from(value: JsonPld) -> Result { + // can we use a serde_ipld_* crate or something to handle most of this for for us? + match value.0 { + serde_json::Value::Null => Ok(Ipld::Null), + serde_json::Value::Bool(b) => Ok(Ipld::Bool(b)), + serde_json::Value::Number(n) => { + if let Some(i) = n.as_i64() { + Ok(Ipld::Integer(i as i128)) + } else if let Some(f) = n.as_f64() { + // should we reject NaN and infinities? + Ok(Ipld::Float(f)) + } else { + anyhow::bail!("Invalid number. Should be unreachable") + } + } + serde_json::Value::String(s) => { + if let Some(str) = s.strip_prefix("ipfs://") { + let cid = Cid::try_from(str)?; + Ok(Ipld::Link(cid)) + } else { + Ok(Ipld::String(s)) + } + } + serde_json::Value::Array(arr) => { + let list = arr + .into_iter() + .map(|v| JsonPld(v).try_into()) + .collect::, _>>()?; + Ok(Ipld::List(list)) + } + serde_json::Value::Object(obj) => { + let map = obj + .into_iter() + .map(|(k, v)| JsonPld(v).try_into().map(|v| (k, v))) + .collect::, _>>()?; + Ok(Ipld::Map(map)) + } + } } } @@ -356,6 +395,8 @@ impl<'a> From<&'a Recipient> for Ipld { mod tests { use std::collections::BTreeMap; + use ipld_core::ipld; + use super::*; struct JwsFixture { @@ -425,7 +466,7 @@ mod tests { } = fixture_jws(); let (payload_b64, protected_b64, signature_b64) = fixture_jws_base64(&payload, &protected, &signature); - let link = Cid::try_from(base64_url::decode(&payload_b64).unwrap()).unwrap(); + let link = Some(Cid::try_from(base64_url::decode(&payload_b64).unwrap()).unwrap()); assert_roundtrip( DagJoseCodec, &JsonWebSignature { @@ -439,6 +480,7 @@ mod tests { signature: signature_b64, }], link, + pld: None, }, &ipld!({ "payload": payload,