use ciborium::Value as CborValue;
pub trait CborValueExt {
fn as_str(&self) -> Option<&str>;
fn as_i64(&self) -> Option<i64>;
fn as_u64(&self) -> Option<u64>;
fn as_f64(&self) -> Option<f64>;
fn get(&self, key: &str) -> Option<&CborValue>;
fn get_mut(&mut self, key: &str) -> Option<&mut CborValue>;
}
impl CborValueExt for CborValue {
fn as_str(&self) -> Option<&str> {
self.as_text()
}
fn as_i64(&self) -> Option<i64> {
self.as_integer().and_then(|i| i64::try_from(i).ok())
}
fn as_u64(&self) -> Option<u64> {
self.as_integer().and_then(|i| u64::try_from(i).ok())
}
fn as_f64(&self) -> Option<f64> {
self.as_float()
}
fn get(&self, key: &str) -> Option<&CborValue> {
self.as_map()?
.iter()
.find(|(k, _)| k.as_text() == Some(key))
.map(|(_, v)| v)
}
fn get_mut(&mut self, key: &str) -> Option<&mut CborValue> {
self.as_map_mut()?
.iter_mut()
.find(|(k, _)| k.as_text() == Some(key))
.map(|(_, v)| v)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn map(pairs: Vec<(&str, CborValue)>) -> CborValue {
CborValue::Map(
pairs
.into_iter()
.map(|(k, v)| (CborValue::Text(k.to_string()), v))
.collect(),
)
}
#[test]
fn as_str_and_int_accessors() {
let v = CborValue::Text("hello".into());
assert_eq!(v.as_str(), Some("hello"));
assert_eq!(v.as_i64(), None);
let n = CborValue::Integer(42i64.into());
assert_eq!(n.as_i64(), Some(42));
assert_eq!(n.as_u64(), Some(42));
assert_eq!(n.as_str(), None);
}
#[test]
fn map_lookup_by_str_key() {
let mut record = map(vec![
("name", CborValue::Text("alice".into())),
("age", CborValue::Integer(30i64.into())),
]);
assert_eq!(record.get("name").and_then(|v| v.as_str()), Some("alice"));
assert_eq!(record.get("age").and_then(|v| v.as_i64()), Some(30));
assert!(record.get("missing").is_none());
if let Some(age) = record.get_mut("age") {
*age = CborValue::Integer(31i64.into());
}
assert_eq!(record.get("age").and_then(|v| v.as_i64()), Some(31));
}
}