1use stepflow_base::{ObjectStoreContent, IdError, generate_id_type};
6use super::InvalidValue;
7use super::value::Value;
8
9generate_id_type!(VarId);
10
11pub trait Var: std::fmt::Debug + stepflow_base::as_any::AsAny {
12 fn id(&self) -> &VarId;
13 fn value_from_str(&self, s: &str) -> Result<Box<dyn Value>, InvalidValue>;
14 fn validate_val_type(&self, val: &Box<dyn Value>) -> Result<(), InvalidValue>;
15}
16
17impl dyn Var + Send + Sync {
19 pub fn downcast<T>(&self) -> Option<&T>
20 where T: Var + std::any::Any
21 {
22 self.as_any().downcast_ref::<T>()
23 }
24 pub fn is<T>(&self) -> bool
25 where T: Var + std::any::Any
26 {
27 self.as_any().is::<T>()
28 }
29}
30
31impl ObjectStoreContent for Box<dyn Var + Sync + Send> {
32 type IdType = VarId;
33
34 fn new_id(id_val: u16) -> Self::IdType {
35 VarId::new(id_val)
36 }
37
38 fn id(&self) -> &Self::IdType {
39 self.as_ref().id()
40 }
41}
42
43macro_rules! define_var {
44 ($name:ident, $valuetype:ident) => {
45
46 #[derive(Debug)]
47 pub struct $name {
48 id: VarId,
49 }
50 impl $name {
51 pub fn new(id: VarId) -> Self {
53 Self { id }
54 }
55
56 pub fn boxed(self) -> Box<dyn Var + Send + Sync> {
58 Box::new(self)
59 }
60 }
61 impl Var for $name {
62 fn id(&self) -> &VarId { &self.id }
64
65 fn value_from_str(&self, s: &str) -> Result<Box<dyn Value>, InvalidValue> {
67 Ok(Box::new(s.parse::<$valuetype>()?) as Box<dyn Value>)
68 }
69
70 fn validate_val_type(&self, val: &Box<dyn Value>) -> Result<(), InvalidValue> {
72 if val.is::<$valuetype>() {
73 Ok(())
74 } else {
75 Err(InvalidValue::WrongType)
76 }
77 }
78 }
79 };
80}
81
82use super::value::EmailValue;
83define_var!(EmailVar, EmailValue);
84
85use super::value::StringValue;
86define_var!(StringVar, StringValue);
87
88use super::value::TrueValue;
89define_var!(TrueVar, TrueValue);
90
91use super::value::BoolValue;
92define_var!(BoolVar, BoolValue);
93
94
95#[cfg(test)]
96pub fn test_var_val() -> (Box<dyn Var + Send + Sync>, Box<dyn Value>) {
97 let var = Box::new(StringVar::new(stepflow_test_util::test_id!(VarId)));
98 let val: Box<dyn Value> = StringValue::try_new("test").unwrap().boxed();
99 (var, val)
100}
101
102#[cfg(test)]
103mod tests {
104 use stepflow_test_util::test_id;
105 use crate::value::{Value, StringValue, EmailValue};
106 use super::{Var, VarId, EmailVar, StringVar, InvalidValue};
107
108 #[test]
109 fn validate_val_type() {
110 let email_addr = "is@email.com";
111 let email_var = EmailVar::new(test_id!(VarId));
112
113 let email_strval: Box<dyn Value> = StringValue::try_new(email_addr).unwrap().boxed();
114 let email_emailval: Box<dyn Value> = EmailValue::try_new(email_addr).unwrap().boxed();
115 assert!(matches!(email_var.validate_val_type(&email_strval), Err(InvalidValue::WrongType)));
116 assert!(matches!(email_var.validate_val_type(&email_emailval), Ok(())));
117 }
118
119 #[test]
120 fn downcast() {
121 let stringvar = StringVar::new(test_id!(VarId));
122 let stringvar_boxed = stringvar.boxed();
123 assert!(matches!(stringvar_boxed.as_any().downcast_ref::<StringVar>(), Some(_)));
124
125 assert!(matches!(stringvar_boxed.downcast::<StringVar>(), Some(_)));
127 assert_eq!(stringvar_boxed.is::<StringVar>(), true);
128 }
129}