Skip to main content

brdd_rust/
lib.rs

1use serde::{Deserialize, Serialize};
2
3/// Standardized error object for BRDD.
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct BrddError {
6    pub code: String,
7    pub message: String,
8}
9
10/// A subset of the context that allows adding errors and checking validity.
11pub trait ValidationContext {
12    fn add_error(&mut self, code: String, message: String);
13    fn is_valid(&self) -> bool;
14    fn get_errors(&self) -> &[BrddError];
15}
16
17/// The central state object passed around and returned by UseCases.
18pub trait ExecutionContext<T>: ValidationContext {
19    fn add_effect(&mut self, code: String);
20    fn add_setter(&mut self, code: String);
21    fn set_data(&mut self, data: T);
22    
23    fn get_data(&self) -> Option<&T>;
24    fn get_effects(&self) -> &[String];
25    fn get_setters(&self) -> &[String];
26    fn get_status(&self) -> u16;
27}
28
29/// The default implementation of the ExecutionContext.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct DefaultExecutionContext<T> {
32    pub data: Option<T>,
33    pub errors: Vec<BrddError>,
34    pub setters: Vec<String>,
35    pub effects: Vec<String>,
36    pub status: u16,
37}
38
39impl<T> DefaultExecutionContext<T> {
40    /// Creates a new execution context with default successful status.
41    pub fn new(initial_data: Option<T>) -> Self {
42        Self {
43            data: initial_data,
44            errors: Vec::new(),
45            setters: Vec::new(),
46            effects: Vec::new(),
47            status: 200,
48        }
49    }
50}
51
52impl<T> ValidationContext for DefaultExecutionContext<T> {
53    fn add_error(&mut self, code: String, message: String) {
54        self.errors.push(BrddError { code, message });
55        self.status = 400; // Default error status
56    }
57
58    fn is_valid(&self) -> bool {
59        self.errors.is_empty()
60    }
61
62    fn get_errors(&self) -> &[BrddError] {
63        &self.errors
64    }
65}
66
67impl<T> ExecutionContext<T> for DefaultExecutionContext<T> {
68    fn add_effect(&mut self, code: String) {
69        self.effects.push(code);
70    }
71
72    fn add_setter(&mut self, code: String) {
73        self.setters.push(code);
74    }
75
76    fn set_data(&mut self, data: T) {
77        self.data = Some(data);
78    }
79
80    fn get_data(&self) -> Option<&T> {
81        self.data.as_ref()
82    }
83
84    fn get_effects(&self) -> &[String] {
85        &self.effects
86    }
87
88    fn get_setters(&self) -> &[String] {
89        &self.setters
90    }
91
92    fn get_status(&self) -> u16 {
93        self.status
94    }
95}
96
97/// Protocol for services dedicated to pure business logic validation.
98pub trait ValidateService<I> {
99    fn validate(&self, ctx: &mut dyn ValidationContext, input: &I);
100}
101
102/// Protocol for services that fetch additional data needed for the UseCase.
103pub trait EnrichService<I, E, T> {
104    fn enrich(&self, ctx: &mut dyn ExecutionContext<T>, input: &I) -> Result<E, BrddError>;
105}
106
107/// Protocol for external adapters (APIs, DBs) to perform side-effects.
108pub trait ClientService<I, T> {
109    fn execute(&self, ctx: &mut dyn ExecutionContext<T>, input: &I);
110}
111
112/// The orchestrator.
113pub trait UseCase<I, O> {
114    fn execute(&self, input: I) -> DefaultExecutionContext<O>;
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_initialization() {
123        let ctx = DefaultExecutionContext::new(Some(1));
124        assert_eq!(ctx.get_data(), Some(&1));
125        assert!(ctx.is_valid());
126        assert_eq!(ctx.get_status(), 200);
127        assert!(ctx.get_errors().is_empty());
128        assert!(ctx.get_effects().is_empty());
129        assert!(ctx.get_setters().is_empty());
130    }
131
132    #[test]
133    fn test_add_error() {
134        let mut ctx: DefaultExecutionContext<i32> = DefaultExecutionContext::new(None);
135        ctx.add_error("R001".to_string(), "Invalid".to_string());
136        
137        assert!(!ctx.is_valid());
138        assert_eq!(ctx.get_status(), 400);
139        assert_eq!(ctx.get_errors().len(), 1);
140        assert_eq!(ctx.get_errors()[0].code, "R001");
141    }
142
143    #[test]
144    fn test_add_effect_and_setter() {
145        let mut ctx: DefaultExecutionContext<i32> = DefaultExecutionContext::new(None);
146        ctx.add_effect("E001".to_string());
147        ctx.add_setter("S001".to_string());
148        
149        assert_eq!(ctx.get_effects().len(), 1);
150        assert_eq!(ctx.get_effects()[0], "E001");
151        assert_eq!(ctx.get_setters().len(), 1);
152        assert_eq!(ctx.get_setters()[0], "S001");
153    }
154
155    #[test]
156    fn test_set_data() {
157        let mut ctx: DefaultExecutionContext<i32> = DefaultExecutionContext::new(None);
158        ctx.set_data(2);
159        assert_eq!(ctx.get_data(), Some(&2));
160    }
161}
162
163pub use brdd_macros::{brdd_use_case, brdd_rule};
164