diff --git a/native/hexpds_dagcbor_internal/Cargo.toml b/native/hexpds_dagcbor_internal/Cargo.toml index fa64d99..d590924 100644 --- a/native/hexpds_dagcbor_internal/Cargo.toml +++ b/native/hexpds_dagcbor_internal/Cargo.toml @@ -10,6 +10,7 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] +libipld = "0.16.0" rustler = "0.31.0" serde_ipld_dagcbor = "0.4.2" serde_json = "1.0.113" diff --git a/native/hexpds_dagcbor_internal/src/lib.rs b/native/hexpds_dagcbor_internal/src/lib.rs index 6d5d5d2..2d97df3 100644 --- a/native/hexpds_dagcbor_internal/src/lib.rs +++ b/native/hexpds_dagcbor_internal/src/lib.rs @@ -1,9 +1,16 @@ use rustler::Encoder; use rustler::Env; +use rustler::NifResult; use rustler::Term; use rustler::Binary; use serde_json::from_str; -use serde_ipld_dagcbor::to_vec; +use serde_json::Value; +use libipld::Ipld; +use libipld::Cid; +use libipld::codec::Codec; +use libipld::cbor::DagCborCodec; +use std::collections::BTreeMap; +use std::str::FromStr; mod atoms { rustler::atoms! { @@ -12,19 +19,64 @@ mod atoms { } } +pub fn json_to_ipld(val: Value) -> Ipld { + match val { + Value::Null => Ipld::Null, + Value::Bool(b) => Ipld::Bool(b), + Value::String(s) => Ipld::String(s), + Value::Number(v) => { + if let Some(f) = v.as_f64() { + if v.is_i64() { + Ipld::Integer(v.as_i64().unwrap().into()) + } else if v.is_u64() { + Ipld::Integer(v.as_i64().unwrap_or_else(|| f as i64).into()) + } else { + Ipld::Float(f) + } + } else { + Ipld::Null + } + }, + Value::Array(l) => Ipld::List(l.into_iter().map(json_to_ipld).collect()), + Value::Object(m) => { + let map: BTreeMap = BTreeMap::from_iter(m.into_iter().map(|(k, v)| { + if k == "cid" && v.is_string() { + (k, Ipld::Link(Cid::from_str(v.as_str().unwrap()).unwrap())) + } else { + (k, json_to_ipld(v)) + } + })); + Ipld::Map(map) + } + } +} + #[rustler::nif] -fn encode_dag_cbor(env: Env, json: String) -> Term { - let parsed_json: serde_json::Value = match from_str(&json) { - Ok(json) => json, - Err(e) => return (atoms::error(), format!("Failed to parse JSON: {}", e)).encode(env), - }; - - let cbor_data = match to_vec(&parsed_json) { - Ok(data) => data, - Err(e) => return (atoms::error(), format!("Failed to encode to DAG-CBOR: {}", e)).encode(env), +fn encode_dag_cbor(env: Env, json: String) -> NifResult { + let parsed_json: serde_json::Value = match from_str(&json) { + Ok(json) => json, + Err(e) => return Ok((atoms::error(), format!("Failed to parse JSON: {}", e)).encode(env)), }; - (atoms::ok(), cbor_data).encode(env) + let ipld_data = json_to_ipld(parsed_json); + + let encoded_dag_cbor = DagCborCodec.encode(&ipld_data); + + match encoded_dag_cbor { + Ok(buffer) => { + let mut binary = rustler::types::binary::OwnedBinary::new(buffer.len()).unwrap(); + + { + let binary_slice = binary.as_mut_slice(); + binary_slice.copy_from_slice(&buffer); + } + + Ok((atoms::ok(), binary.release(env)).encode(env)) + }, + Err(e) => { + return Ok((atoms::error(), format!("Failed to encode to DAG-CBOR: {}", e)).encode(env)); + } + } } #[rustler::nif] @@ -42,4 +94,4 @@ fn decode_dag_cbor<'a>(env: Env<'a>, cbor_data: Binary<'a>) -> Term<'a> { (atoms::ok(), json).encode(env) } -rustler::init!("Elixir.Hexpds.DagCBOR.Internal", [encode_dag_cbor, decode_dag_cbor]); +rustler::init!("Elixir.Hexpds.DagCBOR.Internal", [encode_dag_cbor, decode_dag_cbor]); \ No newline at end of file