1use alloc::boxed::Box;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use crate::compile::CompiledChart;
8use crate::datamodel::{
9 Datamodel, DatamodelRegistry, Environment, EvalError, Event, SessionInfo, StateQuery, Value,
10};
11use crate::ir::Chart;
12use crate::session::{Outcome, StartError, StartInput, start};
13
14pub const NULL_DATAMODEL: &str = "null";
16
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
22pub struct NullDatamodel;
23
24impl NullDatamodel {
25 pub fn new() -> Self {
27 NullDatamodel
28 }
29}
30
31pub fn parse_in_predicate(expr: &str) -> Option<&str> {
34 let e = expr.trim();
35 let inner = e.strip_prefix("In")?.trim_start();
36 let inner = inner.strip_prefix('(')?.strip_suffix(')')?.trim();
37 let quote = inner.chars().next()?;
38 if quote != '\'' && quote != '"' {
39 return None;
40 }
41 let id = inner.strip_prefix(quote)?.strip_suffix(quote)?;
42 if id.is_empty() || id.contains(quote) {
43 return None;
44 }
45 Some(id)
46}
47
48fn unsupported(what: &str) -> EvalError {
49 EvalError::new(alloc::format!("the null datamodel does not support {what}"))
50}
51
52impl Datamodel for NullDatamodel {
53 fn name(&self) -> &str {
54 NULL_DATAMODEL
55 }
56
57 fn init_session(&mut self, _info: &SessionInfo) -> Result<(), EvalError> {
58 Ok(())
59 }
60
61 fn set_environment(&mut self, _env: &Environment) {}
62
63 fn declare(&mut self, _id: &str) -> Result<(), EvalError> {
64 Err(unsupported("data"))
65 }
66
67 fn set_event(&mut self, _event: &Event) {}
68
69 fn eval_cond(&mut self, expr: &str, states: &dyn StateQuery) -> Result<bool, EvalError> {
70 match parse_in_predicate(expr) {
71 Some(id) => Ok(states.in_state(id)),
72 None => Err(unsupported("conditions other than In()")),
73 }
74 }
75
76 fn eval_value(&mut self, _expr: &str, _states: &dyn StateQuery) -> Result<Value, EvalError> {
77 Err(unsupported("value expressions"))
78 }
79
80 fn eval_content(&mut self, content: &str) -> Result<Value, EvalError> {
81 Ok(Value::String(String::from(content)))
82 }
83
84 fn assign_expr(
85 &mut self,
86 _location: &str,
87 _expr: &str,
88 _states: &dyn StateQuery,
89 ) -> Result<(), EvalError> {
90 Err(unsupported("assign"))
91 }
92
93 fn assign_value(&mut self, _location: &str, _value: Value) -> Result<(), EvalError> {
94 Err(unsupported("assign"))
95 }
96
97 fn bind_var(&mut self, _name: &str, _value: Value) -> Result<(), EvalError> {
98 Err(unsupported("variables"))
99 }
100
101 fn is_location(&mut self, _location: &str) -> bool {
102 false
103 }
104
105 fn exec_script(&mut self, _script: &str, _states: &dyn StateQuery) -> Result<(), EvalError> {
106 Err(unsupported("script"))
107 }
108
109 fn foreach_items(
110 &mut self,
111 _array: &str,
112 _states: &dyn StateQuery,
113 ) -> Result<Vec<Value>, EvalError> {
114 Err(unsupported("foreach"))
115 }
116
117 fn snapshot(&self) -> Value {
120 Value::Undefined
121 }
122
123 fn restore(&mut self, snapshot: &Value) -> Result<(), EvalError> {
124 match snapshot {
125 Value::Undefined => Ok(()),
126 _ => Err(EvalError::new("a null datamodel snapshot is undefined")),
127 }
128 }
129}
130
131#[derive(Clone, Copy, Debug, Default)]
133pub struct NullRegistry;
134
135impl DatamodelRegistry for NullRegistry {
136 fn create(&self, name: &str) -> Option<Box<dyn Datamodel>> {
137 if name == NULL_DATAMODEL || name.is_empty() {
138 Some(Box::new(NullDatamodel))
139 } else {
140 None
141 }
142 }
143}
144
145#[derive(Clone, Debug, Default)]
147pub struct WithNull<R>(pub R);
148
149impl<R: DatamodelRegistry> DatamodelRegistry for WithNull<R> {
150 fn create(&self, name: &str) -> Option<Box<dyn Datamodel>> {
151 self.0.create(name).or_else(|| NullRegistry.create(name))
152 }
153
154 fn create_for(&self, chart: &Chart) -> Option<Result<Box<dyn Datamodel>, Vec<String>>> {
155 self.0
156 .create_for(chart)
157 .or_else(|| NullRegistry.create_for(chart))
158 }
159
160 fn check(&self, chart: &Chart) -> Result<(), Vec<String>> {
161 self.0.check(chart)
162 }
163}
164
165pub fn create_datamodel(
170 registry: &dyn DatamodelRegistry,
171 name: &str,
172) -> Result<Box<dyn Datamodel>, StartError> {
173 let name = if name.is_empty() {
174 NULL_DATAMODEL
175 } else {
176 name
177 };
178 registry
179 .create(name)
180 .or_else(|| NullRegistry.create(name))
181 .ok_or_else(|| StartError::UnsupportedDatamodel {
182 name: String::from(name),
183 })
184}
185
186pub fn create_datamodel_for(
193 registry: &dyn DatamodelRegistry,
194 chart: &Chart,
195) -> Result<Box<dyn Datamodel>, StartError> {
196 if chart.datamodel.is_empty() {
197 return create_datamodel(registry, &chart.datamodel);
198 }
199 match registry.create_for(chart) {
200 Some(Ok(dm)) => Ok(dm),
201 Some(Err(problems)) => Err(StartError::ChartRefused {
202 datamodel: chart.datamodel.clone(),
203 problems,
204 }),
205 None => create_datamodel(registry, &chart.datamodel),
206 }
207}
208
209pub fn start_with_registry(
212 chart: &CompiledChart,
213 registry: &dyn DatamodelRegistry,
214 input: StartInput,
215) -> Result<(Outcome, Box<dyn Datamodel>), StartError> {
216 let mut dm = create_datamodel_for(registry, chart.chart())?;
217 let outcome = start(chart, input, dm.as_mut())?;
218 Ok((outcome, dm))
219}