object-rainbow-json 0.0.0-a.22

serde_json for object-rainbow
Documentation
use std::collections::BTreeMap;

use futures_util::future::try_join_all;
use object_rainbow::{
    Enum, Fetch, InlineOutput, ListHashes, MaybeHasNiche, NicheForUnsized, NoNiche, Parse,
    ParseInline, Tagged, ToOutput, Topological, length_prefixed::LpString, numeric::Le,
};
use object_rainbow_point::{IntoPoint, Point};
use serde::{Deserialize, Serialize};

#[derive(
    Enum,
    ToOutput,
    InlineOutput,
    ListHashes,
    Topological,
    Parse,
    ParseInline,
    Clone,
    Default,
    Serialize,
    Deserialize,
)]
#[serde(untagged)]
#[topology(recursive)]
pub enum Distributed {
    #[default]
    Null,
    Bool(bool),
    I64(Le<i64>),
    U64(Le<u64>),
    F64(Le<f64>),
    String(Point<String>),
    Array(#[parse(unchecked)] Point<Vec<Self>>),
    Object(#[parse(unchecked)] Point<BTreeMap<LpString, Self>>),
}

impl Tagged for Distributed {}
impl MaybeHasNiche for Distributed {
    type MnArray = NoNiche<NicheForUnsized>;
}

impl Distributed {
    pub async fn to_value(&self) -> object_rainbow::Result<serde_json::Value> {
        Ok(match *self {
            Distributed::Null => serde_json::Value::Null,
            Distributed::Bool(x) => x.into(),
            Distributed::I64(x) => x.0.into(),
            Distributed::U64(x) => x.0.into(),
            Distributed::F64(x) => x.0.into(),
            Distributed::String(ref point) => point.fetch().await?.into(),
            Distributed::Array(ref point) => try_join_all(
                point
                    .fetch()
                    .await?
                    .into_iter()
                    .map(async |x| x.to_value().await),
            )
            .await?
            .into(),
            Distributed::Object(ref point) => {
                try_join_all(
                    point.fetch().await?.into_iter().map(async |(k, x)| {
                        Ok::<_, object_rainbow::Error>((k.0, x.to_value().await?))
                    }),
                )
                .await?
                .into_iter()
                .collect::<serde_json::Map<_, _>>()
                .into()
            }
        })
    }
}

#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum DistributedParseError {
    #[error("invalid number")]
    InvalidNumber,
}

impl TryFrom<serde_json::Value> for Distributed {
    type Error = DistributedParseError;

    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
        Ok(match value {
            serde_json::Value::Null => Self::Null,
            serde_json::Value::Bool(x) => Self::Bool(x),
            serde_json::Value::Number(x) => {
                if let Some(x) = x.as_u64() {
                    Self::U64(x.into())
                } else if let Some(x) = x.as_i64() {
                    Self::I64(x.into())
                } else if let Some(x) = x.as_f64() {
                    Self::F64(x.into())
                } else {
                    return Err(DistributedParseError::InvalidNumber);
                }
            }
            serde_json::Value::String(x) => Self::String(x.point()),
            serde_json::Value::Array(vec) => Self::Array(
                vec.into_iter()
                    .map(Self::try_from)
                    .collect::<Result<Vec<_>, _>>()?
                    .point(),
            ),
            serde_json::Value::Object(map) => Self::Object(
                map.into_iter()
                    .map(|(k, v)| Ok((LpString(k), Self::try_from(v)?)))
                    .collect::<Result<BTreeMap<_, _>, _>>()?
                    .point(),
            ),
        })
    }
}