1use crate::schema::CommandSchema;
2use serde::Serialize;
3
4#[derive(Debug, Clone, Serialize)]
6pub struct OperationDescription {
7 pub operation: String,
8 pub params: serde_json::Value,
9 pub description: String,
10}
11
12pub trait Operation<T, E>: Send + Sync {
14 fn name(&self) -> &str;
16
17 fn apply(&self, input: T) -> Result<T, E>;
19
20 fn describe(&self) -> OperationDescription;
22
23 fn schema() -> CommandSchema
25 where
26 Self: Sized;
27}
28
29#[derive(Debug, Serialize)]
31pub struct PipelinePlan {
32 pub steps: Vec<OperationDescription>,
33}
34
35pub struct Pipeline<T, E> {
37 operations: Vec<Box<dyn Operation<T, E>>>,
38}
39
40impl<T, E> Pipeline<T, E> {
41 pub fn new() -> Self {
42 Self {
43 operations: Vec::new(),
44 }
45 }
46
47 pub fn push<O: Operation<T, E> + 'static>(mut self, op: O) -> Self {
48 self.operations.push(Box::new(op));
49 self
50 }
51
52 pub fn push_boxed(mut self, op: Box<dyn Operation<T, E>>) -> Self {
53 self.operations.push(op);
54 self
55 }
56
57 pub fn execute(&self, mut input: T) -> Result<T, E> {
59 for op in &self.operations {
60 input = op.apply(input)?;
61 }
62 Ok(input)
63 }
64
65 pub fn describe(&self) -> PipelinePlan {
67 PipelinePlan {
68 steps: self.operations.iter().map(|op| op.describe()).collect(),
69 }
70 }
71
72 pub fn len(&self) -> usize {
73 self.operations.len()
74 }
75
76 pub fn is_empty(&self) -> bool {
77 self.operations.is_empty()
78 }
79}
80
81impl<T, E> Default for Pipeline<T, E> {
82 fn default() -> Self {
83 Self::new()
84 }
85}