acts 0.19.0

a fast, lightweight, extensiable workflow engine
Documentation
use crate::{Act, Branch, Step, Vars, Workflow};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::sync::{Arc, RwLock, Weak};

use super::{node_tree, visit::VisitRoot};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NodeContent {
    Workflow(Workflow),
    Branch(Branch),
    Step(Step),
    Act(Act),
}

#[derive(PartialEq, Default, Debug, Clone, Serialize, Deserialize)]
pub enum NodeKind {
    #[default]
    Workflow,
    Branch,
    Step,
    Act,
}

#[derive(PartialEq, Default, Copy, Debug, Clone, Serialize, Deserialize)]
pub enum NodeOutputKind {
    #[default]
    Normal,
    Catch,
    Timeout,
}

#[derive(Debug, Clone)]
pub struct NodeOutput {
    pub typ: NodeOutputKind,
    pub node: Arc<Node>,
}

#[derive(Clone)]
pub struct Node {
    pub id: String,
    pub content: NodeContent,
    pub level: usize,
    pub parent: Arc<RwLock<Weak<Node>>>,
    pub children: Arc<RwLock<Vec<NodeOutput>>>,
    pub prev: Arc<RwLock<Weak<Node>>>,
    pub next: Arc<RwLock<Weak<Node>>>,

    // nodes created dynamically
    pub nodes: Arc<RwLock<Vec<Arc<Node>>>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeData {
    pub id: String,
    pub content: NodeContent,
    pub level: usize,
    // pub parent: Option<String>,
    // pub children: Vec<String>,
    // pub prev: Option<String>,
    // pub next: Option<String>,
}

impl NodeContent {
    pub fn id(&self) -> String {
        match self {
            NodeContent::Workflow(data) => data.id.clone(),
            NodeContent::Branch(data) => data.id.clone(),
            NodeContent::Step(data) => data.id.clone(),
            NodeContent::Act(data) => data.id.to_string(),
        }
    }

    pub fn name(&self) -> String {
        match self {
            NodeContent::Workflow(data) => data.name.clone(),
            NodeContent::Branch(data) => data.name.clone(),
            NodeContent::Step(data) => data.name.clone(),
            NodeContent::Act(data) => data.name.to_string(),
        }
    }

    pub fn vars(&self) -> Vars {
        match self {
            NodeContent::Workflow(data) => data.vars(),
            NodeContent::Branch(data) => data.vars(),
            NodeContent::Step(data) => data.vars(),
            NodeContent::Act(data) => data.vars(),
        }
    }

    pub fn options(&self) -> Vars {
        match self {
            NodeContent::Workflow(node) => node.options.clone(),
            NodeContent::Branch(node) => node.options.clone(),
            NodeContent::Step(node) => node.options.clone(),
            NodeContent::Act(node) => node.options.clone(),
        }
    }

    pub fn params(&self) -> serde_json::Value {
        match self {
            NodeContent::Step(node) => node.params.clone(),
            NodeContent::Act(node) => node.params.clone(),
            _ => serde_json::Value::Null,
        }
    }

    pub fn tag(&self) -> String {
        match self {
            NodeContent::Workflow(node) => node.tag.clone(),
            NodeContent::Branch(node) => node.tag.clone(),
            NodeContent::Step(node) => node.tag.clone(),
            NodeContent::Act(node) => node.tag.clone(),
        }
    }

    pub fn r#if(&self) -> Option<String> {
        match self {
            NodeContent::Step(node) => node.r#if.clone(),
            NodeContent::Branch(node) => node.r#if.clone(),
            NodeContent::Act(node) => node.r#if.clone(),
            _ => None,
        }
    }

    pub fn set_if(&mut self, v: Option<String>) {
        match self {
            NodeContent::Step(node) => node.r#if = v,
            NodeContent::Branch(node) => node.r#if = v,
            NodeContent::Act(node) => node.r#if = v,
            _ => {}
        }
    }
}

impl Node {
    pub fn new(id: &str, data: NodeContent, level: usize) -> Self {
        Self {
            id: id.to_string(),
            content: data,
            level,
            parent: Arc::new(RwLock::new(Weak::new())),
            children: Arc::new(RwLock::new(Vec::new())),
            prev: Arc::new(RwLock::new(Weak::new())),
            next: Arc::new(RwLock::new(Weak::new())),
            nodes: Arc::new(RwLock::new(Vec::new())),
        }
    }

    pub fn parent(&self) -> Option<Arc<Node>> {
        let node = self.parent.read().unwrap();
        if let Some(parent) = node.upgrade() {
            return Some(parent);
        }

        if let Some(prev) = self.prev().upgrade() {
            return prev.parent();
        }

        None
    }

    pub fn push_child(&self, typ: NodeOutputKind, child: &Arc<Node>) {
        let mut children = self.children.write().unwrap();
        children.push(NodeOutput {
            typ,
            node: child.clone(),
        });
    }

    pub fn set_parent(&self, parent: &Arc<Node>) {
        self.set_parent_in(NodeOutputKind::Normal, parent);
    }

    /// set parent in the node tree with the given type
    pub fn set_parent_in(&self, typ: NodeOutputKind, parent: &Arc<Node>) {
        *self.parent.write().unwrap() = Arc::downgrade(parent);
        parent.children.write().unwrap().push(NodeOutput {
            typ,
            node: Arc::new(self.clone()),
        });
    }

    pub fn set_next(self: &Arc<Node>, node: &Arc<Node>, is_prev: bool) {
        *self.next.write().unwrap() = Arc::downgrade(node);
        if is_prev {
            *node.prev.write().unwrap() = Arc::downgrade(self);
        }
    }

    pub fn append_node(self: &Arc<Node>, id: &str, data: NodeContent, level: usize) -> Arc<Node> {
        let node = Arc::new(Node::new(id, data.clone(), level));
        self.nodes.write().unwrap().push(node.clone());
        node
    }

    pub fn children(&self) -> Vec<Arc<Node>> {
        self.children_in(NodeOutputKind::Normal)
    }

    pub fn children_in(&self, typ: NodeOutputKind) -> Vec<Arc<Node>> {
        let node = self.children.read().unwrap();
        node.iter()
            .filter(|n| n.typ == typ)
            .map(|n| n.node.clone())
            .collect::<Vec<_>>()
    }

    pub fn next(&self) -> Weak<Node> {
        let next = self.next.read().unwrap();
        next.clone()
    }

    pub fn prev(&self) -> Weak<Node> {
        let prev = self.prev.read().unwrap();
        prev.clone()
    }

    pub fn id(&self) -> &str {
        &self.id
    }

    pub fn uses(&self) -> String {
        match &self.content {
            NodeContent::Step(step) => step.uses.clone().unwrap_or_default(),
            NodeContent::Act(act) => act.uses.to_string(),
            _ => "".to_string(),
        }
    }

    pub fn params(&self) -> Value {
        match &self.content {
            NodeContent::Step(step) => step.params.clone(),
            NodeContent::Act(act) => act.params.clone(),
            _ => Value::Null,
        }
    }

    pub fn name(&self) -> String {
        self.content.name()
    }

    pub fn kind(&self) -> NodeKind {
        match &self.content {
            NodeContent::Workflow(_) => NodeKind::Workflow,
            NodeContent::Branch(_) => NodeKind::Branch,
            NodeContent::Step(_) => NodeKind::Step,
            NodeContent::Act(_) => NodeKind::Act,
        }
    }

    pub fn tag(&self) -> String {
        self.content.tag()
    }

    #[allow(clippy::inherent_to_string)]
    pub fn to_string(&self) -> String {
        let data = NodeData {
            id: self.id.clone(),
            content: self.content.clone(),
            level: self.level,
        };
        serde_json::to_string(&data).unwrap()
    }

    pub fn from_str(s: &str, tree: &node_tree::NodeTree) -> Arc<Self> {
        let data: NodeData = serde_json::from_str(s).unwrap();
        let ret = Arc::new(Self::new(&data.id, data.content, data.level));
        if let Some(node) = tree.node(&ret.id) {
            return node;
        }
        // for c in &data.children {
        //     if let Some(n) = tree.node(c) {
        //         ret.push_child(&n);
        //     }
        // }
        // if let Some(parent) = &data.parent {
        //     if let Some(n) = tree.node(parent) {
        //         ret.set_parent(&n);
        //     }
        // }

        // if let Some(prev) = &data.prev {
        //     if let Some(n) = tree.node(prev) {
        //         ret.set_prev(&n, false);
        //     }
        // }

        // if let Some(next) = &data.next {
        //     if let Some(n) = tree.node(next) {
        //         ret.set_next(&n, false);
        //     }
        // }

        ret
    }

    #[allow(unused)]
    pub fn print(self: &Arc<Self>) {
        VisitRoot::walk(self, &move |n| {
            // print single line
            if n.level > 0 {
                for index in 1..n.level {
                    if n.path.contains_key(&index) {
                        if n.path[&index] {
                            print!("");
                        } else {
                            print!("    ");
                        }
                    }
                }
                if n.is_last {
                    print!("└── ");
                } else {
                    print!("├── ");
                }
            }
            let next = match n.next().upgrade() {
                Some(n) => n.id().to_string(),
                None => "nil".to_string(),
            };

            if n.kind() == NodeKind::Act {
                println!(
                    "{}:{} uses={} name={}  next={}",
                    n.kind(),
                    n.id(),
                    n.uses(),
                    n.name(),
                    next,
                );
            } else {
                println!("{}:{} name={}  next={}", n.kind(), n.id(), n.name(), next);
            }
        });
    }
}

impl std::fmt::Debug for Node {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Node")
            .field("data", &self.content)
            .field("level", &self.level)
            .field("parent", &self.parent)
            .field("children", &self.children)
            .field("next", &self.next)
            .finish()
    }
}

impl std::fmt::Display for NodeKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self {
            NodeKind::Workflow => "workflow",
            NodeKind::Branch => "branch",
            NodeKind::Step => "step",
            NodeKind::Act => "act",
        };
        f.write_str(s)
    }
}

impl From<NodeKind> for String {
    fn from(value: NodeKind) -> Self {
        value.to_string()
    }
}

impl From<String> for NodeKind {
    fn from(kind: String) -> Self {
        let s: &str = &kind;
        s.into()
    }
}

impl From<&str> for NodeKind {
    fn from(str: &str) -> Self {
        match str {
            "workflow" => NodeKind::Workflow,
            "branch" => NodeKind::Branch,
            "step" => NodeKind::Step,
            "act" => NodeKind::Act,
            _ => panic!("not found NodeKind: {str}"),
        }
    }
}