photonic-dynamic 0.1.1

Dynamic light controller and animator
Documentation
use anyhow::{anyhow, Context, Result};
use palette::rgb::Rgb;
use serde::de::DeserializeOwned;
use std::marker::PhantomData;

use photonic::attr::{AsFixedAttr, Bounded};
use photonic::boxed::{Boxed, BoxedBoundAttrDecl, BoxedFreeAttrDecl, BoxedNodeDecl, BoxedOutputDecl};
use photonic::input::InputValue;
use photonic::scene::InputHandle;
use photonic::{input, AttrValue, NodeHandle, Scene};

use crate::config;
use crate::registry::Registry;

pub struct InputBuilder<'b, Reg: Registry>(&'b mut Builder<Reg>);

pub struct AttrBuilder<'b, Reg: Registry>(&'b mut Builder<Reg>);

pub struct NodeBuilder<'b, Reg: Registry>(&'b mut Builder<Reg>);

pub struct OutputBuilder<'b, Reg: Registry>(&'b mut Builder<Reg>);

impl<Reg: Registry> InputBuilder<'_, Reg> {
    pub fn input<I>(&mut self, config: config::Input) -> Result<InputHandle<I>>
    where I: InputValue {
        return self.0.input(config);
    }
}

impl<Reg: Registry> AttrBuilder<'_, Reg> {
    pub fn input<I>(&mut self, config: config::Input) -> Result<InputHandle<I>>
    where I: InputValue {
        return self.0.input(config);
    }

    pub fn free_attr<V>(&mut self, name: &str, config: config::Attr<V>) -> Result<BoxedFreeAttrDecl<V>>
    where V: AttrValue + input::Coerced + DeserializeOwned {
        return self.0.free_attr(name, config);
    }

    pub fn bound_attr<V>(&mut self, name: &str, config: config::Attr<V>) -> Result<BoxedBoundAttrDecl<V>>
    where V: AttrValue + input::Coerced + DeserializeOwned + Bounded {
        return self.0.bound_attr(name, config);
    }
}

impl<Reg: Registry> NodeBuilder<'_, Reg> {
    pub fn node(&mut self, name: &str, config: config::Node) -> Result<NodeHandle<BoxedNodeDecl<Rgb>>> {
        return self.0.node(name, config);
    }

    pub fn input<I>(&mut self, config: config::Input) -> Result<InputHandle<I>>
    where I: InputValue {
        return self.0.input(config);
    }

    pub fn free_attr<V>(&mut self, name: &str, config: config::Attr<V>) -> Result<BoxedFreeAttrDecl<V>>
    where V: AttrValue + input::Coerced + DeserializeOwned {
        return self.0.free_attr(name, config);
    }

    pub fn bound_attr<V>(&mut self, name: &str, config: config::Attr<V>) -> Result<BoxedBoundAttrDecl<V>>
    where V: AttrValue + input::Coerced + DeserializeOwned + Bounded {
        return self.0.bound_attr(name, config);
    }
}

impl<Reg: Registry> OutputBuilder<'_, Reg> {
    pub fn output(&mut self, config: config::Output) -> Result<BoxedOutputDecl> {
        return self.0.output(config);
    }
}

pub struct Builder<Reg> {
    scene: Scene,
    registry: PhantomData<Reg>,
}

impl<Reg: Registry> Default for Builder<Reg> {
    fn default() -> Self {
        Self::new()
    }
}

impl<Reg: Registry> Builder<Reg> {
    pub fn new() -> Self {
        let scene = Scene::new();

        return Self {
            scene,
            registry: PhantomData,
        };
    }

    pub fn build(self) -> Scene {
        return self.scene;
    }

    fn input<I>(&mut self, config: config::Input) -> Result<InputHandle<I>>
    where I: InputValue {
        return self.scene.input(&config.input).with_context(|| format!("Failed to build input: {}", config.input));
    }

    fn free_attr<V>(&mut self, name: &str, config: config::Attr<V>) -> Result<BoxedFreeAttrDecl<V>>
    where V: AttrValue + input::Coerced + DeserializeOwned {
        match config {
            config::Attr::Attr {
                kind,
                config,
            } => {
                let factory =
                    Reg::free_attr::<Reg, V>(&kind).ok_or_else(|| anyhow!("Unknown attribute type: {}", kind))?;

                let decl = factory
                    .produce(config, AttrBuilder(self))
                    .with_context(|| format!("Failed to build attribute: (type={kind}) @{name}"))?;

                return Ok(decl);
            }

            config::Attr::Input {
                input,
                initial,
            } => {
                let input: InputHandle<V::Input> =
                    self.input(input).with_context(|| format!("Failed to build input: @{name}"))?;
                return Ok(input.attr(initial).boxed());
            }

            config::Attr::Fixed(value) => {
                let attr = V::fixed(value);
                return Ok(Box::new(attr));
            }
        }
    }

    fn bound_attr<V>(&mut self, name: &str, config: config::Attr<V>) -> Result<BoxedBoundAttrDecl<V>>
    where V: AttrValue + input::Coerced + DeserializeOwned + Bounded {
        match config {
            config::Attr::Attr {
                kind,
                config,
            } => {
                let factory =
                    Reg::bound_attr::<Reg, V>(&kind).ok_or_else(|| anyhow!("Unknown attribute type: {}", kind))?;

                let decl = factory
                    .produce(config, AttrBuilder(self))
                    .with_context(|| format!("Failed to build attribute: (type={kind}) @{name}"))?;

                return Ok(decl);
            }

            config::Attr::Input {
                input,
                initial,
            } => {
                let input: InputHandle<V::Input> =
                    self.input(input).with_context(|| format!("Failed to build input: @{name}"))?;
                return Ok(input.attr(initial).boxed());
            }

            config::Attr::Fixed(value) => {
                let attr = V::fixed(value);
                return Ok(Box::new(attr));
            }
        }
    }

    pub fn node(&mut self, name: &str, config: config::Node) -> Result<NodeHandle<BoxedNodeDecl<Rgb>>> {
        let factory = Reg::node::<Reg>(&config.kind).ok_or_else(|| anyhow!("Unknown node type: {}", config.kind))?;

        let decl = factory
            .produce(config.config, NodeBuilder(self))
            .with_context(|| format!("Failed to build node: {} (type={}) @{}", config.name, config.kind, name))?;

        return self.scene.node(&config.name, decl);
    }

    pub fn output(&mut self, config: config::Output) -> Result<BoxedOutputDecl> {
        let factory =
            Reg::output::<Reg>(&config.kind).ok_or_else(|| anyhow!("Unknown output type: {}", config.kind))?;

        let decl = factory
            .produce(config.config, OutputBuilder(self))
            .with_context(|| format!("Failed to build output: (type={})", config.kind))?;

        return Ok(decl);
    }
}