use std::io::Cursor;
use ciborium::Value;
use crate::error::{
Error,
Result,
};
pub fn encode(value: &Value) -> Result<Vec<u8>> {
let mut buf = Vec::<u8>::new();
ciborium::into_writer(value, &mut buf).map_err(|err| Error::Cbor(err.to_string()))?;
Ok(buf)
}
pub fn decode(bytes: &[u8]) -> Result<Value> {
let mut cursor = Cursor::new(bytes);
let value =
ciborium::from_reader::<Value, _>(&mut cursor).map_err(|err| Error::Cbor(err.to_string()))?;
let consumed = usize::try_from(cursor.position())
.map_err(|_| Error::Cbor("cursor position out of usize range".into()))?;
if consumed != bytes.len() {
return Err(Error::Cbor(format!(
"trailing bytes after CBOR value ({consumed}/{} consumed)",
bytes.len()
)));
}
Ok(value)
}
#[must_use]
pub fn get_int_field(value: &Value, key: i128) -> Option<&Value> {
value
.as_map()?
.iter()
.find_map(|&(ref stored_key, ref stored_val)| {
let stored = Into::<i128>::into(stored_key.as_integer()?);
(stored == key).then_some(stored_val)
})
}
#[must_use]
pub fn get_text_field<'a>(value: &'a Value, key: &str) -> Option<&'a Value> {
value
.as_map()?
.iter()
.find_map(|&(ref stored_key, ref stored_val)| {
(stored_key.as_text()? == key).then_some(stored_val)
})
}
pub fn require_bytes<'a>(value: &'a Value, label: &'static str) -> Result<&'a [u8]> {
value
.as_bytes()
.map(Vec::as_slice)
.ok_or(Error::Parse(label))
}
#[must_use]
pub fn take_int_field(entries: &mut Vec<(Value, Value)>, key: i128) -> Option<Value> {
let pos = entries
.iter()
.position(|&(ref stored, _)| stored.as_integer().map(Into::<i128>::into) == Some(key))?;
Some(entries.swap_remove(pos).1)
}
#[must_use]
pub fn take_text_field(entries: &mut Vec<(Value, Value)>, key: &str) -> Option<Value> {
let pos = entries
.iter()
.position(|&(ref stored, _)| stored.as_text() == Some(key))?;
Some(entries.swap_remove(pos).1)
}