use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrddError {
pub code: String,
pub message: String,
}
pub trait ValidationContext {
fn add_error(&mut self, code: String, message: String);
fn is_valid(&self) -> bool;
fn get_errors(&self) -> &[BrddError];
}
pub trait ExecutionContext<T>: ValidationContext {
fn add_effect(&mut self, code: String);
fn add_setter(&mut self, code: String);
fn set_data(&mut self, data: T);
fn get_data(&self) -> Option<&T>;
fn get_effects(&self) -> &[String];
fn get_setters(&self) -> &[String];
fn get_status(&self) -> u16;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DefaultExecutionContext<T> {
pub data: Option<T>,
pub errors: Vec<BrddError>,
pub setters: Vec<String>,
pub effects: Vec<String>,
pub status: u16,
}
impl<T> DefaultExecutionContext<T> {
pub fn new(initial_data: Option<T>) -> Self {
Self {
data: initial_data,
errors: Vec::new(),
setters: Vec::new(),
effects: Vec::new(),
status: 200,
}
}
}
impl<T> ValidationContext for DefaultExecutionContext<T> {
fn add_error(&mut self, code: String, message: String) {
self.errors.push(BrddError { code, message });
self.status = 400; }
fn is_valid(&self) -> bool {
self.errors.is_empty()
}
fn get_errors(&self) -> &[BrddError] {
&self.errors
}
}
impl<T> ExecutionContext<T> for DefaultExecutionContext<T> {
fn add_effect(&mut self, code: String) {
self.effects.push(code);
}
fn add_setter(&mut self, code: String) {
self.setters.push(code);
}
fn set_data(&mut self, data: T) {
self.data = Some(data);
}
fn get_data(&self) -> Option<&T> {
self.data.as_ref()
}
fn get_effects(&self) -> &[String] {
&self.effects
}
fn get_setters(&self) -> &[String] {
&self.setters
}
fn get_status(&self) -> u16 {
self.status
}
}
pub trait ValidateService<I> {
fn validate(&self, ctx: &mut dyn ValidationContext, input: &I);
}
pub trait EnrichService<I, E, T> {
fn enrich(&self, ctx: &mut dyn ExecutionContext<T>, input: &I) -> Result<E, BrddError>;
}
pub trait ClientService<I, T> {
fn execute(&self, ctx: &mut dyn ExecutionContext<T>, input: &I);
}
pub trait UseCase<I, O> {
fn execute(&self, input: I) -> DefaultExecutionContext<O>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initialization() {
let ctx = DefaultExecutionContext::new(Some(1));
assert_eq!(ctx.get_data(), Some(&1));
assert!(ctx.is_valid());
assert_eq!(ctx.get_status(), 200);
assert!(ctx.get_errors().is_empty());
assert!(ctx.get_effects().is_empty());
assert!(ctx.get_setters().is_empty());
}
#[test]
fn test_add_error() {
let mut ctx: DefaultExecutionContext<i32> = DefaultExecutionContext::new(None);
ctx.add_error("R001".to_string(), "Invalid".to_string());
assert!(!ctx.is_valid());
assert_eq!(ctx.get_status(), 400);
assert_eq!(ctx.get_errors().len(), 1);
assert_eq!(ctx.get_errors()[0].code, "R001");
}
#[test]
fn test_add_effect_and_setter() {
let mut ctx: DefaultExecutionContext<i32> = DefaultExecutionContext::new(None);
ctx.add_effect("E001".to_string());
ctx.add_setter("S001".to_string());
assert_eq!(ctx.get_effects().len(), 1);
assert_eq!(ctx.get_effects()[0], "E001");
assert_eq!(ctx.get_setters().len(), 1);
assert_eq!(ctx.get_setters()[0], "S001");
}
#[test]
fn test_set_data() {
let mut ctx: DefaultExecutionContext<i32> = DefaultExecutionContext::new(None);
ctx.set_data(2);
assert_eq!(ctx.get_data(), Some(&2));
}
}
pub use brdd_macros::{brdd_use_case, brdd_rule};