use ciborium::value::{Integer as CborInt, Value as CborValue};
use serde_json::{value::Number as JsonNumber, Value as JsonValue};
fn cbor_text_into_string(cbor: CborValue) -> Option<String> {
if let CborValue::Text(text) = cbor {
Some(text)
} else {
None
}
}
pub fn cbor_to_json(cbor: CborValue) -> Option<JsonValue> {
Some(match cbor {
CborValue::Null => JsonValue::Null,
CborValue::Bool(boolean) => JsonValue::Bool(boolean),
CborValue::Text(string) => JsonValue::String(string),
CborValue::Integer(int) => JsonValue::Number({
let int: i128 = int.into();
if let Ok(int) = u64::try_from(int) {
JsonNumber::from(int)
} else if let Ok(int) = i64::try_from(int) {
JsonNumber::from(int)
} else {
JsonNumber::from_f64(int as f64)?
}
}),
CborValue::Float(float) => JsonValue::Number(JsonNumber::from_f64(float)?),
CborValue::Array(vec) => {
JsonValue::Array(vec.into_iter().map(cbor_to_json).collect::<Option<_>>()?)
}
CborValue::Map(map) => JsonValue::Object(
map.into_iter()
.map(|(k, v)| Some((cbor_text_into_string(k)?, cbor_to_json(v)?)))
.collect::<Option<_>>()?,
),
CborValue::Bytes(bytes) => bytes.into(),
CborValue::Tag(_, value) => cbor_to_json(*value)?,
_ => unimplemented!(),
})
}
pub fn json_to_cbor(json: JsonValue) -> CborValue {
match json {
JsonValue::Null => CborValue::Null,
JsonValue::Bool(boolean) => CborValue::Bool(boolean),
JsonValue::String(string) => CborValue::Text(string),
JsonValue::Number(number) => {
if let Some(number) = number.as_u64() {
CborValue::Integer(CborInt::from(number))
} else if let Some(number) = number.as_i64() {
CborValue::Integer(CborInt::from(number))
} else if let Some(number) = number.as_f64() {
CborValue::Float(number)
} else {
unreachable!()
}
}
JsonValue::Array(vec) => CborValue::Array(vec.into_iter().map(json_to_cbor).collect()),
JsonValue::Object(map) => CborValue::Map(
map.into_iter()
.map(|(k, v)| (CborValue::Text(k), json_to_cbor(v)))
.collect(),
),
}
}