this repo has no description
at main 2.9 kB view raw
1//! convert an ipld_core::ipld::Ipld enum into a serde_json::value::Value in the atproto data model 2//! 3//! a specific helper is required for this as Bytes and Link have differing representations to how serde_json handles them by default 4//! 5//! in general. types are naievely converted. the following types have special cases: 6//! - `integer`: this could throw an error if the number is `x` in `i64::MIN < x < u64::MAX` 7//! - `float`: always issues a warning since this is technically illegal. If its NaN or infinity, this errors as they cant be represented in json 8//! - `bytes`: atproto JSON represents them as `{"$bytes": "BASE 64 NO PADDING"}`, but serde_json defaults to `[u8]` 9//! - `link`: atproto JSON represents them as `{"$link": "BASE 32 NO PADDING"}`, but serde_json defaults to `[u8]` 10 11use base64::{Engine, prelude::BASE64_STANDARD_NO_PAD}; 12use ipld_core::{cid::multibase::Base, ipld::Ipld}; 13use log::warn; 14use serde_json::{Map, Number, Value, json}; 15use thiserror::Error; 16 17#[derive(Error, Debug)] 18pub enum Error { 19 #[error("CID error: {}", .0)] 20 Cid(#[from] ipld_core::cid::Error), 21 #[error("Number too big: {0} > {1} || {0} < {2}", .val, u64::MAX, i64::MIN)] 22 IntInvalidSize { val: i128 }, 23 #[error("{} was NaN or Infinity", .0)] 24 FloatInvalidSize(f64), 25} 26 27/// Convert any decoded IPLD data into serde_json Value 28/// 29/// This can be used for `sqlx` queries or decoded into plain JSON 30/// 31/// We can't use bog standard `to_string` or `to_json` functions 32/// due to the fact that ATProto DAG-CBOR does not map straight 33/// to ATProto JSON (see: `Ipld::Bytes` and `Ipld::Link` in the function definition.) 34/// 35/// To get an atproto JSON representation in string format call 36/// `Value::to_string(&self)` on the Value from this function 37pub fn ipld_to_json_value(data: &Ipld) -> Result<Value, Error> { 38 Ok(match data { 39 Ipld::Null => Value::Null, 40 Ipld::Bool(bool) => Value::Bool(*bool), 41 Ipld::Integer(int) => { 42 Value::Number(Number::from_i128(*int).ok_or(Error::IntInvalidSize { val: *int })?) 43 } 44 Ipld::Float(float) => { 45 warn!("Got float in IPLD data: {}", float); 46 Value::Number(Number::from_f64(*float).ok_or(Error::FloatInvalidSize(*float))?) 47 } 48 Ipld::String(str) => Value::String(str.clone()), 49 Ipld::Bytes(items) => json!({ "$bytes": BASE64_STANDARD_NO_PAD.encode(items) }), 50 Ipld::List(iplds) => Value::Array( 51 iplds 52 .iter() 53 .map(ipld_to_json_value) 54 .collect::<Result<Vec<_>, _>>()?, 55 ), 56 Ipld::Map(map) => Value::Object( 57 map.iter() 58 .map(|(k, v)| Ok::<_, Error>((k.clone(), ipld_to_json_value(v)?))) 59 .collect::<Result<Map<String, Value>, _>>()?, 60 ), 61 Ipld::Link(cid) => json!({ 62 "$link": cid.to_string_of_base(Base::Base32Lower)? 63 }), 64 }) 65}