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}