use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
pub use crate::json::Value;
pub trait Field: Sized + Default {
fn put(&self) -> Value;
fn take(v: Option<&Value>) -> Self;
}
pub trait Json: Sized {
fn to_json(&self) -> Value;
fn from_json(v: &Value) -> Option<Self>;
}
impl Field for String {
fn put(&self) -> Value {
Value::string(self.as_str())
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_str).map(String::from).unwrap_or_default()
}
}
impl Field for bool {
fn put(&self) -> Value {
Value::Bool(*self)
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_bool).unwrap_or(false)
}
}
impl Field for u64 {
fn put(&self) -> Value {
Value::u64(*self)
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_u64).unwrap_or(0)
}
}
impl Field for i64 {
fn put(&self) -> Value {
Value::i64(*self)
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_i64).unwrap_or(0)
}
}
impl Field for u32 {
fn put(&self) -> Value {
Value::u64(*self as u64)
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_u64).map_or(0, |x| x.min(u32::MAX as u64) as u32)
}
}
impl Field for i32 {
fn put(&self) -> Value {
Value::i64(*self as i64)
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_i64).map_or(0, |x| x.clamp(i32::MIN as i64, i32::MAX as i64) as i32)
}
}
impl Field for u16 {
fn put(&self) -> Value {
Value::u64(*self as u64)
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_u64).map_or(0, |x| x.min(u16::MAX as u64) as u16)
}
}
impl Field for u8 {
fn put(&self) -> Value {
Value::u64(*self as u64)
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_u64).map_or(0, |x| x.min(255) as u8)
}
}
impl Default for Value {
fn default() -> Value {
Value::Null
}
}
impl Field for Value {
fn put(&self) -> Value {
self.clone()
}
fn take(v: Option<&Value>) -> Self {
v.cloned().unwrap_or(Value::Null)
}
}
impl<T: Field> Field for Vec<T> {
fn put(&self) -> Value {
Value::Array(self.iter().map(Field::put).collect())
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_array).map(|a| a.iter().map(|x| T::take(Some(x))).collect()).unwrap_or_default()
}
}
impl<T: Field> Field for Option<T> {
fn put(&self) -> Value {
match self {
Some(x) => x.put(),
None => Value::Null,
}
}
fn take(v: Option<&Value>) -> Self {
match v {
None | Some(Value::Null) => None,
Some(x) => Some(T::take(Some(x))),
}
}
}
impl<T: Field> Field for BTreeMap<String, T> {
fn put(&self) -> Value {
Value::Object(self.iter().map(|(k, v)| (k.clone(), v.put())).collect())
}
fn take(v: Option<&Value>) -> Self {
v.and_then(Value::as_object).map(|o| o.iter().map(|(k, x)| (k.clone(), T::take(Some(x)))).collect()).unwrap_or_default()
}
}
pub fn empty_object() -> Value {
Value::Object(Vec::new())
}
#[macro_export]
macro_rules! record {
($(#[$m:meta])* pub struct $name:ident { $( $(#[$fm:meta])* pub $f:ident : $t:ty ),* $(,)? }) => {
$(#[$m])*
#[derive(Clone, Debug, Default, PartialEq)]
pub struct $name { $( $(#[$fm])* pub $f: $t ),* }
impl $crate::jmap::Json for $name {
fn to_json(&self) -> $crate::jmap::Value {
#[allow(unused_mut)]
let mut o = $crate::jmap::empty_object();
$( o.insert(stringify!($f), $crate::jmap::Field::put(&self.$f)); )*
o
}
fn from_json(v: &$crate::jmap::Value) -> Option<Self> {
v.as_object()?;
Some($name { $( $f: $crate::jmap::Field::take(v.get(stringify!($f))) ),* })
}
}
impl $crate::jmap::Field for $name {
fn put(&self) -> $crate::jmap::Value {
$crate::jmap::Json::to_json(self)
}
fn take(v: Option<&$crate::jmap::Value>) -> Self {
v.and_then(<$name as $crate::jmap::Json>::from_json).unwrap_or_default()
}
}
};
}
#[macro_export]
macro_rules! wire_enum {
($(#[$m:meta])* pub enum $name:ident { $first:ident = $fs:literal $(, $v:ident = $s:literal)* $(,)? }) => {
$(#[$m])*
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum $name { #[default] $first $(, $v)* }
impl $name {
pub const ALL: &'static [$name] = &[$name::$first $(, $name::$v)*];
pub fn as_str(&self) -> &'static str {
match self { $name::$first => $fs $(, $name::$v => $s)* }
}
pub fn parse(s: &str) -> Option<$name> {
match s { $fs => Some($name::$first) $(, $s => Some($name::$v))*, _ => None }
}
}
impl $crate::jmap::Field for $name {
fn put(&self) -> $crate::jmap::Value {
$crate::jmap::Value::string(self.as_str())
}
fn take(v: Option<&$crate::jmap::Value>) -> Self {
v.and_then($crate::jmap::Value::as_str).and_then($name::parse).unwrap_or_default()
}
}
};
}
pub fn list<T: Json>(items: &[T]) -> Value {
Value::Array(items.iter().map(Json::to_json).collect())
}
pub fn from_list<T: Json>(v: &Value) -> Vec<T> {
let arr = v.as_array().or_else(|| v.get("items").and_then(Value::as_array));
arr.map(|a| a.iter().filter_map(T::from_json).collect()).unwrap_or_default()
}
pub fn str_of<'a>(v: &'a Value, k: &str) -> &'a str {
v.get(k).and_then(Value::as_str).unwrap_or("")
}
pub fn u64_of(v: &Value, k: &str) -> u64 {
v.get(k).and_then(Value::as_u64).unwrap_or(0)
}
pub fn obj<const N: usize>(fields: [(&str, Value); N]) -> Value {
Value::object(fields)
}