lumen-engine 0.2.1

Core composition model and rendering orchestration for Lumen.
Documentation
use super::{
    ids::NodeId,
    property::{PropertyExpression, PropertyValue},
    schema::PropertyDef,
};

pub struct DelegateEvalContext<'a> {
    pub node_id: NodeId,
    pub property_path: &'a str,
    pub expr: &'a crate::expr::ExpressionContext<'a>,
}

impl<'a> DelegateEvalContext<'a> {
    pub fn child(&'a self, property_path: &'a str) -> Self {
        Self {
            node_id: self.node_id,
            property_path,
            expr: self.expr,
        }
    }
}

pub trait DelegateEvaluable: Clone {
    type Evaluated;

    fn eval(&self, ctx: &DelegateEvalContext<'_>) -> crate::Result<Self::Evaluated>;
}

pub trait DelegateValue: DelegateEvaluable {
    fn to_property_value(&self) -> PropertyValue;
    fn to_property_expression(&self) -> PropertyExpression {
        PropertyExpression::Value(self.to_property_value())
    }
    fn from_property_expression(value: PropertyExpression) -> crate::Result<Self>
    where
        Self: Sized;
}

pub trait Delegated: Clone + Default + Sized {
    type Delegate: Clone;

    fn into_delegate(self) -> Self::Delegate;
}

pub struct NodeParamEvalContext<'a> {
    pub node_id: NodeId,
    pub expr: &'a crate::expr::ExpressionContext<'a>,
}

pub trait NodeParams: Clone + Default {
    type Evaluated;

    fn property_defs() -> Vec<PropertyDef>;
    fn is_property(id: &str) -> bool;
    fn default_properties(&self) -> Vec<(&'static str, PropertyValue)>;
    fn get_property(&self, id: &str) -> Option<PropertyExpression>;
    fn eval(&self, ctx: &NodeParamEvalContext<'_>) -> crate::Result<Self::Evaluated>;

    #[cfg(feature = "json")]
    fn from_json(
        params: Option<&serde_json::Map<String, serde_json::Value>>,
    ) -> anyhow::Result<Self>
    where
        Self: Sized,
        Self: serde::de::DeserializeOwned,
    {
        let value = params
            .cloned()
            .map(serde_json::Value::Object)
            .unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new()));
        serde_path_to_error::deserialize(value).map_err(|error| anyhow::anyhow!(error.to_string()))
    }
}

#[derive(Debug, Clone, Default)]
pub struct DelegateVec<T: Delegated> {
    pub items: Vec<T::Delegate>,
}

#[cfg(feature = "json")]
impl<'de, T> serde::Deserialize<'de> for DelegateVec<T>
where
    T: Delegated,
    T::Delegate: serde::Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        <Vec<T::Delegate> as serde::Deserialize>::deserialize(deserializer)
            .map(|items| Self { items })
    }
}

impl<T> DelegateEvaluable for DelegateVec<T>
where
    T: Delegated,
    T::Delegate: DelegateEvaluable<Evaluated = T>,
{
    type Evaluated = Vec<T>;

    fn eval(&self, ctx: &DelegateEvalContext<'_>) -> crate::Result<Self::Evaluated> {
        self.items
            .iter()
            .enumerate()
            .map(|(index, value)| {
                let path = format!("{}[{index}]", ctx.property_path);
                value.eval(&ctx.child(&path))
            })
            .collect()
    }
}

impl<T> Delegated for Vec<T>
where
    T: Delegated,
{
    type Delegate = DelegateVec<T>;

    fn into_delegate(self) -> Self::Delegate {
        DelegateVec {
            items: self.into_iter().map(Delegated::into_delegate).collect(),
        }
    }
}