photonic 0.1.1

Dynamic light controller and animator
Documentation
use std::borrow::Borrow;
use std::collections::HashMap;
use std::future::Future;
use std::hash::Hash;
use std::sync::{Arc, OnceLock};

use anyhow::Result;
use futures::Stream;

use crate::attr::AttrValueType;
use crate::input::{AnyInputValue, InputSink, InputValueType};
use crate::utils::TreeIterator;

#[derive(Debug)]
pub struct NodeInfoBuilder {
    pub key: String,

    pub kind: &'static str,
    pub name: String,

    pub nodes: HashMap<String, NodeInfoBuilder>,
    pub attrs: HashMap<String, AttrInfoBuilder>,
}

#[derive(Debug)]
pub struct AttrInfoBuilder {
    pub key: String,

    pub kind: &'static str,
    pub value_type: AttrValueType,

    pub attrs: HashMap<String, AttrInfoBuilder>,
    pub inputs: HashMap<String, InputInfoBuilder>,
}

#[derive(Debug)]
pub struct InputInfoBuilder {
    pub key: String,

    pub name: String,
    pub value_type: InputValueType,

    pub sink: InputSink,
}

#[derive(Debug)]
pub struct NodeInfo {
    key: String,

    kind: &'static str,
    name: String,

    parent: Option<Arc<NodeInfo>>,

    nodes: OnceLock<HashMap<String, Arc<NodeInfo>>>,
    attrs: OnceLock<HashMap<String, Arc<AttrInfo>>>,
}

impl NodeInfo {
    pub fn key(&self) -> &str {
        return &self.key;
    }

    pub fn kind(&self) -> &'static str {
        return self.kind;
    }

    pub fn name(&self) -> &str {
        return &self.name;
    }

    pub fn parent(&self) -> Option<&Arc<NodeInfo>> {
        return self.parent.as_ref();
    }

    pub fn nodes(&self) -> &HashMap<String, Arc<NodeInfo>> {
        return self.nodes.get().expect("set");
    }

    pub fn attrs(&self) -> &HashMap<String, Arc<AttrInfo>> {
        return self.attrs.get().expect("set");
    }

    pub fn find_attr<'i, I, Q>(&self, mut path: I) -> Option<&AttrInfo>
    where
        I: Iterator<Item = &'i Q>,
        Q: Hash + Eq + 'i,
        String: Borrow<Q>,
    {
        return match path.next() {
            None => None,
            Some(attr) => self.attrs.get().expect("set").get(attr)?.find_attr(path),
        };
    }
}

#[derive(Debug)]
pub struct AttrInfo {
    key: String,

    kind: &'static str,
    value_type: AttrValueType,

    node: Arc<NodeInfo>,
    parent: Option<Arc<AttrInfo>>,

    attrs: OnceLock<HashMap<String, Arc<AttrInfo>>>,
    inputs: OnceLock<HashMap<String, Arc<InputInfo>>>,
}

impl AttrInfo {
    pub fn key(&self) -> &str {
        return &self.key;
    }

    pub fn kind(&self) -> &'static str {
        return self.kind;
    }

    pub fn value_type(&self) -> AttrValueType {
        return self.value_type;
    }

    pub fn node(&self) -> &Arc<NodeInfo> {
        return &self.node;
    }

    pub fn parent(&self) -> Option<&Arc<AttrInfo>> {
        return self.parent.as_ref();
    }

    pub fn find_attr<'i, I, Q>(&self, mut path: I) -> Option<&Self>
    where
        I: Iterator<Item = &'i Q>,
        Q: Hash + Eq + 'i,
        String: Borrow<Q>,
    {
        return match path.next() {
            None => Some(self),
            Some(attr) => self.attrs.get().expect("set").get(attr)?.find_attr(path),
        };
    }

    pub fn attrs(&self) -> &HashMap<String, Arc<AttrInfo>> {
        return self.attrs.get().expect("set");
    }

    pub fn inputs(&self) -> &HashMap<String, Arc<InputInfo>> {
        return self.inputs.get().expect("set");
    }
}

#[derive(Debug)]
pub struct InputInfo {
    key: String,

    name: String,
    value_type: InputValueType,

    node: Arc<NodeInfo>,
    attr: Arc<AttrInfo>,

    sink: InputSink,
}

impl InputInfo {
    pub fn key(&self) -> &str {
        return &self.key;
    }

    pub fn name(&self) -> &str {
        return &self.name;
    }

    pub fn value_type(&self) -> InputValueType {
        return self.value_type;
    }

    pub fn node(&self) -> &Arc<NodeInfo> {
        return &self.node;
    }

    pub fn attr(&self) -> &Arc<AttrInfo> {
        return &self.attr;
    }

    pub fn sink(&self) -> &InputSink {
        return &self.sink;
    }

    pub fn subscribe(&self) -> impl Stream<Item = AnyInputValue> + Send + Unpin {
        return self.sink.subscribe();
    }
}

pub struct Introspection {
    pub root: Arc<NodeInfo>,

    pub nodes: HashMap<String, Arc<NodeInfo>>,
    pub inputs: HashMap<String, Arc<InputInfo>>,
}

impl Introspection {
    pub fn with(root: NodeInfoBuilder) -> Arc<Self> {
        fn build_input(builder: InputInfoBuilder, node: Arc<NodeInfo>, attr: Arc<AttrInfo>) -> Arc<InputInfo> {
            return Arc::new(InputInfo {
                key: builder.key,
                name: builder.name,
                value_type: builder.value_type,
                node,
                attr,
                sink: builder.sink,
            });
        }

        fn build_attr(builder: AttrInfoBuilder, node: Arc<NodeInfo>, parent: Option<Arc<AttrInfo>>) -> Arc<AttrInfo> {
            let result = Arc::new(AttrInfo {
                key: builder.key,
                kind: builder.kind,
                value_type: builder.value_type,
                node: node.clone(),
                parent,
                attrs: OnceLock::new(),
                inputs: OnceLock::new(),
            });

            result
                .attrs
                .set(
                    builder
                        .attrs
                        .into_iter()
                        .map(|(key, attr)| (key, build_attr(attr, node.clone(), Some(result.clone()))))
                        .collect(),
                )
                .expect("unset");

            result
                .inputs
                .set(
                    builder
                        .inputs
                        .into_iter()
                        .map(|(key, input)| (key, build_input(input, node.clone(), result.clone())))
                        .collect(),
                )
                .expect("unset");

            return result;
        }

        fn build_node(builder: NodeInfoBuilder, parent: Option<Arc<NodeInfo>>) -> Arc<NodeInfo> {
            let result = Arc::new(NodeInfo {
                key: builder.key,
                kind: builder.kind,
                name: builder.name,
                parent,
                nodes: OnceLock::new(),
                attrs: OnceLock::new(),
            });

            result
                .nodes
                .set(
                    builder
                        .nodes
                        .into_iter()
                        .map(|(key, node)| (key, build_node(node, Some(result.clone()))))
                        .collect(),
                )
                .expect("unset");

            result
                .attrs
                .set(
                    builder
                        .attrs
                        .into_iter()
                        .map(|(key, attr)| (key, build_attr(attr, result.clone(), None)))
                        .collect(),
                )
                .expect("unset");

            return result;
        }

        let root = build_node(root, None);

        let nodes = TreeIterator::new(&root, |node| node.nodes().values())
            .map(|node| (node.name.clone(), node.clone()))
            .collect::<HashMap<_, _>>();

        let inputs = TreeIterator::new(&root, |node| node.nodes().values())
            .flat_map(|node| node.attrs().values())
            .flat_map(|attr| TreeIterator::new(attr, |attr| attr.attrs().values()))
            .flat_map(|attr| attr.inputs().values())
            .map(|input| (input.name.clone(), input.clone()))
            .collect();

        return Arc::new(Self {
            root,
            nodes,
            inputs,
        });
    }

    pub fn log(&self) {
        eprintln!("🔎 Full scene:");

        fn log_input(depth: usize, key: &str, input: &InputInfo) {
            let indent = String::from("  ").repeat(depth);
            eprintln!("🔎  {indent}🎛 {} = {}", key, input.name);
        }

        fn log_attr(depth: usize, key: &str, attr: &AttrInfo) {
            let indent = String::from("  ").repeat(depth);
            eprintln!("🔎  {indent}🪛 {} = {}:{} {{", key, attr.value_type, attr.kind);

            for (name, attr) in attr.attrs() {
                log_attr(depth + 1, name, attr);
            }

            for (name, input) in attr.inputs() {
                log_input(depth + 1, name, input);
            }

            eprintln!("🔎  {indent}}}");
        }

        fn log_node(depth: usize, key: &str, node: &NodeInfo) {
            let indent = String::from("  ").repeat(depth);
            eprintln!("🔎  {indent}{} = {}@{} {{", key, node.kind, node.name);

            for (name, attr) in node.attrs() {
                log_attr(depth + 1, name, attr);
            }

            for (name, node) in node.nodes() {
                log_node(depth + 1, name, node);
            }

            eprintln!("🔎  {indent}}}")
        }

        log_node(0, "root", &self.root);
    }
}

pub trait Interface {
    fn listen(self, introspection: Arc<Introspection>) -> impl Future<Output = Result<()>> + Send + 'static;
}