use super::Value; use anyhow::{Context, Result, anyhow, bail}; use std::collections::BTreeMap; pub trait FromValue: Sized { fn from_value(v: Value) -> Result; } impl Value { pub fn parse(self) -> Result { T::from_value(self) } pub fn as_class(self, name: &'static str) -> Result { if let Value::Object { class, fields } = self { if class == name { Ok(Fields { class: name, fields, }) } else { bail!("expected class {name} but found {class}") } } else { bail!("expected class {name} but found something else") } } } pub struct Fields { class: &'static str, fields: BTreeMap, } impl Fields { pub fn field(&mut self, name: &str) -> Result { self.fields .remove(name) .ok_or(anyhow!("expected {name} field in {}", self.class))? .parse() .context(anyhow!("in {}.{name} field", self.class)) } pub fn remove(&mut self, key: &str) -> Option { self.fields.remove(key) } } impl FromValue for u8 { fn from_value(v: Value) -> anyhow::Result { v.as_u8().ok_or(anyhow!("expected u8")) } } impl FromValue for u16 { fn from_value(v: Value) -> anyhow::Result { v.as_u16().ok_or(anyhow!("expected u16")) } } impl FromValue for f32 { fn from_value(v: Value) -> anyhow::Result { v.as_f32().ok_or(anyhow!("expected f32")) } } impl FromValue for u32 { fn from_value(v: Value) -> anyhow::Result { v.as_u32().ok_or(anyhow!("expected u32")) } } impl FromValue for i32 { fn from_value(v: Value) -> anyhow::Result { v.as_i32().ok_or(anyhow!("expected i32")) } } impl FromValue for u64 { fn from_value(v: Value) -> anyhow::Result { v.as_u64().ok_or(anyhow!("expected u64")) } } impl FromValue for bool { fn from_value(v: Value) -> anyhow::Result { v.as_bool().ok_or(anyhow!("expected bool")) } } impl FromValue for String { fn from_value(v: Value) -> anyhow::Result { v.as_string().ok_or(anyhow!("expected string")) } } impl Value { pub fn as_vector(self) -> Option> { self.as_class("vector").ok()?.remove("Array")?.as_array() } } impl FromValue for BTreeMap { fn from_value(v: Value) -> Result { v.as_class("map")? .remove("Array") .ok_or(anyhow!("map is missing Array field"))? .as_array() .ok_or(anyhow!("map Array field is not an array"))? .into_iter() .map(|e| { let mut fields = e.as_class("pair")?; Ok((fields.field("first")?, fields.field("second")?)) }) .collect::>>() } }