anchor_chain/
chain.rs

1//! Provides structures for creating and executing chains.
2//!
3//! This module defines `Chain` and `ChainBuilder` structures for building chains of
4//! operations where each operation is represented by a `Node`. These chains
5//! facilitate asynchronous processing of data from an initial input to a final output.
6
7use async_trait::async_trait;
8use std::fmt;
9use std::marker::PhantomData;
10
11use crate::error::AnchorChainError;
12use crate::{link::Link, node::Node};
13
14/// Represents a chain of nodes that can asynchronously process data.
15///
16/// `Chain` is constructed from a sequence of `Node` instances, each taking an input
17/// and producing an output. The output of one node serves as the input to the next,
18/// allowing for a flexible and composable approach to complex asynchronous processing tasks.
19#[derive(Debug)]
20pub struct Chain<I, O, L> {
21    link: L,
22    _input: PhantomData<I>,
23    _output: PhantomData<O>,
24}
25
26impl<I, O, L> Chain<I, O, L>
27where
28    L: Node<Input = I, Output = O> + Send + Sync + std::fmt::Debug,
29    I: std::fmt::Debug,
30    O: std::fmt::Debug,
31{
32    /// Creates a new `Chain` from the provided initial link.
33    ///
34    /// `Link` serves as a container for chaining two `Node` instances together,
35    /// where the output of the first node is fed as the input to the next. These
36    /// links can be nested to create a chain of nodes.
37    pub fn new(link: L) -> Self {
38        Chain {
39            link,
40            _input: PhantomData,
41            _output: PhantomData,
42        }
43    }
44
45    /// Asynchronously processes the provided input through the chain of nodes.
46    ///
47    /// The input is processed by each node in the chain, with the output of one node
48    /// serving as the input to the next. The final output of the chain is returned.
49    /// If any node in the chain returns an error, the processing is halted and
50    /// the error is returned.
51    pub async fn process(&self, input: I) -> Result<O, AnchorChainError> {
52        self.link.process(input).await
53    }
54}
55
56#[async_trait]
57impl<I, O, L> Node for Chain<I, O, L>
58where
59    L: Node<Input = I, Output = O> + Send + Sync + fmt::Debug,
60    I: fmt::Debug + Send + Sync,
61    O: fmt::Debug + Send + Sync,
62{
63    type Input = I;
64    type Output = O;
65
66    async fn process(&self, input: Self::Input) -> Result<Self::Output, AnchorChainError> {
67        self.process(input).await
68    }
69}
70
71/// A builder for constructing a `Chain` of nodes.
72///
73/// `ChainBuilder` allows for incremental construction of a processing chain, adding
74/// node one at a time. This approach facilitates clear and concise assembly
75/// of complex processing logic.
76pub struct ChainBuilder {}
77
78impl ChainBuilder {
79    /// Creates a new `ChainBuilder` instance.
80    pub fn new() -> Self {
81        ChainBuilder {}
82    }
83
84    /// Adds the first node to the chain.
85    pub fn link<I, N>(self, node: N) -> LinkedChainBuilder<I, N>
86    where
87        N: Node<Input = I> + Send + Sync + std::fmt::Debug,
88        I: Send,
89    {
90        LinkedChainBuilder {
91            link: node,
92            _input: PhantomData,
93        }
94    }
95}
96
97impl Default for ChainBuilder {
98    fn default() -> Self {
99        ChainBuilder::new()
100    }
101}
102
103/// A builder for constructing a `Chain` of nodes using Link.
104///
105/// `LinkedChainBuilder` takes an initial node and allows for incremental
106/// construction of a processing chain, adding nodes one at a time. New nodes
107/// are linked to the previous nodes using nested `Link` instances.
108pub struct LinkedChainBuilder<I, L> {
109    link: L,
110    _input: PhantomData<I>,
111}
112
113impl<I, L> LinkedChainBuilder<I, L>
114where
115    L: Node<Input = I> + Send + Sync + std::fmt::Debug,
116    I: Send,
117{
118    /// Adds a new node to the chain, linking it to the previous node.
119    pub fn link<N>(self, next: N) -> LinkedChainBuilder<I, Link<L, N>>
120    where
121        N: Node<Input = L::Output> + Send + Sync + std::fmt::Debug,
122        L::Output: Send,
123        Link<L, N>: Node<Input = I>,
124    {
125        LinkedChainBuilder {
126            link: Link {
127                node: self.link,
128                next,
129            },
130            _input: PhantomData,
131        }
132    }
133
134    /// Finalizes the construction of the chain, returning a `Chain` instance
135    /// ready for processing.
136    pub fn build(self) -> Chain<I, L::Output, L>
137    where
138        L: Node,
139    {
140        Chain {
141            link: self.link,
142            _input: PhantomData,
143            _output: PhantomData,
144        }
145    }
146}