use crate::utils::json::Json;
use crate::utils::sign::msgpack::{Map, Value};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum Unpackable {
#[error("{0} is not a whole number; the venue hashes decimals as strings, so write it in quotes")]
Fraction(String),
#[error("{0} does not fit in 64 bits")]
TooWide(String),
#[error("an action is an object, and this is {0}")]
NotAnObject(&'static str),
}
impl Json {
pub fn to_msgpack(&self) -> Result<Value, Unpackable> {
Ok(match self {
Json::Null => Value::Nil,
Json::Bool(value) => Value::Bool(*value),
Json::Number(digits) => whole(digits)?,
Json::String(text) => Value::from(text.clone()),
Json::Array(items) => Value::Array(items.iter().map(Json::to_msgpack).collect::<Result<_, _>>()?),
Json::Object(entries) => Value::Map(map_of(entries)?),
})
}
pub fn to_action(&self) -> Result<Map, Unpackable> {
match self {
Json::Object(entries) => map_of(entries),
other => Err(Unpackable::NotAnObject(other.kind())),
}
}
}
fn map_of(entries: &[(String, Json)]) -> Result<Map, Unpackable> {
let mut map = Map::new();
for (key, value) in entries {
map = map.put(key.clone(), value.to_msgpack()?);
}
Ok(map)
}
fn whole(digits: &str) -> Result<Value, Unpackable> {
if digits.contains(['.', 'e', 'E']) {
return Err(Unpackable::Fraction(digits.to_owned()));
}
match digits.strip_prefix('-') {
Some(_) => digits.parse::<i64>().map(Value::Int).map_err(|_| Unpackable::TooWide(digits.to_owned())),
None => digits.parse::<u64>().map(Value::Uint).map_err(|_| Unpackable::TooWide(digits.to_owned())),
}
}
#[cfg(test)]
#[path = "tests/msgpack.rs"]
mod tests;