1use crate::planning::semantics::{
4 LiteralValue as DomainLiteralValue, SemanticDateTime, SemanticTime,
5 ValueKind as DomainValueKind,
6};
7use crate::result_value::{
8 CalendarResult as DomainCalendarResult, RangeResult as DomainRangeResult,
9 RuleResultValue as DomainRuleResultValue,
10};
11use indexmap::IndexMap;
12use rust_decimal::Decimal;
13use serde::{Deserialize, Serialize};
14use std::collections::BTreeMap;
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "lowercase")]
19pub enum ValueKind {
20 Number(Decimal),
21 Measure {
22 value: Decimal,
23 },
24 Ratio {
25 value: Decimal,
26 },
27 Text(String),
28 Date(SemanticDateTime),
29 Time(SemanticTime),
30 Boolean(bool),
31 Range {
32 from: Box<ValueKind>,
33 to: Box<ValueKind>,
34 },
35}
36
37impl ValueKind {
38 fn from_non_range(value: &DomainValueKind) -> Self {
39 match value {
40 DomainValueKind::Number(rational) => Self::Number(
41 rational
42 .try_to_decimal()
43 .expect("BUG: planned number must convert to decimal"),
44 ),
45 DomainValueKind::Measure(rational) => Self::Measure {
46 value: rational
47 .try_to_decimal()
48 .expect("BUG: planned measure must convert to decimal"),
49 },
50 DomainValueKind::Ratio(rational) => Self::Ratio {
51 value: rational
52 .try_to_decimal()
53 .expect("BUG: planned ratio must convert to decimal"),
54 },
55 DomainValueKind::Text(text) => Self::Text(text.clone()),
56 DomainValueKind::Date(date) => Self::Date(date.clone()),
57 DomainValueKind::Time(time) => Self::Time(time.clone()),
58 DomainValueKind::Boolean(flag) => Self::Boolean(*flag),
59 DomainValueKind::Range(_, _) => {
60 unreachable!("BUG: range payloads are converted by ValueKind::from")
61 }
62 DomainValueKind::Spec(_) => {
63 unreachable!("BUG: spec values are rule results, not nested ValueKind payloads")
64 }
65 DomainValueKind::List(_) => {
66 unreachable!("BUG: list values are rule results, not nested ValueKind payloads")
67 }
68 }
69 }
70}
71
72enum ValueKindTask<'a> {
73 Visit(&'a DomainValueKind),
74 BuildRange,
75}
76
77impl From<&DomainValueKind> for ValueKind {
78 fn from(value: &DomainValueKind) -> Self {
79 let mut tasks = vec![ValueKindTask::Visit(value)];
80 let mut converted: Vec<ValueKind> = Vec::new();
81 while let Some(task) = tasks.pop() {
82 match task {
83 ValueKindTask::Visit(DomainValueKind::Range(from, to)) => {
84 tasks.push(ValueKindTask::BuildRange);
85 tasks.push(ValueKindTask::Visit(&to.value));
86 tasks.push(ValueKindTask::Visit(&from.value));
87 }
88 ValueKindTask::Visit(other) => converted.push(ValueKind::from_non_range(other)),
89 ValueKindTask::BuildRange => {
90 let to = converted
91 .pop()
92 .expect("BUG: range endpoint converted before build");
93 let from = converted
94 .pop()
95 .expect("BUG: range endpoint converted before build");
96 converted.push(Self::Range {
97 from: Box::new(from),
98 to: Box::new(to),
99 });
100 }
101 }
102 }
103 converted
104 .pop()
105 .expect("BUG: value conversion yields exactly one ValueKind")
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub struct LiteralValue {
111 pub value: ValueKind,
112}
113
114impl From<&DomainLiteralValue> for LiteralValue {
115 fn from(literal: &DomainLiteralValue) -> Self {
116 Self {
117 value: ValueKind::from(&literal.value),
118 }
119 }
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
123pub struct CalendarResult {
124 pub value: Decimal,
125 pub unit: String,
126}
127
128impl From<&DomainCalendarResult> for CalendarResult {
129 fn from(calendar: &DomainCalendarResult) -> Self {
130 Self {
131 value: calendar.value,
132 unit: calendar.unit.clone(),
133 }
134 }
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
138pub struct RangeResult {
139 pub from: RuleResultValue,
140 pub to: RuleResultValue,
141}
142
143impl From<&DomainRangeResult> for RangeResult {
144 fn from(range: &DomainRangeResult) -> Self {
145 Self {
146 from: RuleResultValue::from(&range.from),
147 to: RuleResultValue::from(&range.to),
148 }
149 }
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
153pub struct RuleResultValue {
154 #[serde(default, skip_serializing_if = "Option::is_none")]
155 pub result: Option<String>,
156 #[serde(default, skip_serializing_if = "Option::is_none")]
157 pub measure: Option<BTreeMap<String, Decimal>>,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub ratio: Option<BTreeMap<String, Decimal>>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
161 pub number: Option<Decimal>,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
163 pub boolean: Option<bool>,
164 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub text: Option<String>,
166 #[serde(default, skip_serializing_if = "Option::is_none")]
167 pub date: Option<SemanticDateTime>,
168 #[serde(default, skip_serializing_if = "Option::is_none")]
169 pub time: Option<SemanticTime>,
170 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub calendar: Option<CalendarResult>,
172 #[serde(default, skip_serializing_if = "Option::is_none")]
173 pub unit: Option<String>,
174 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub range: Option<Box<RangeResult>>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
177 pub spec: Option<Box<crate::result_value::SpecResult>>,
178 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub row: Option<crate::result_value::RowReference>,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
181 pub list: Option<Vec<RuleResultValue>>,
182 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub fields: Option<IndexMap<String, RuleResultValue>>,
184 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub reference: Option<String>,
188}
189
190struct RuleResultFrame {
193 shell: RuleResultValue,
194 has_range: bool,
195 list_len: Option<usize>,
196 field_names: Option<Vec<String>>,
197 child_count: usize,
198}
199
200enum RuleResultTask<'a> {
201 Visit(&'a DomainRuleResultValue),
202 Assemble(Box<RuleResultFrame>),
203}
204
205impl From<&DomainRuleResultValue> for RuleResultValue {
206 fn from(value: &DomainRuleResultValue) -> Self {
207 let mut tasks = vec![RuleResultTask::Visit(value)];
208 let mut converted: Vec<RuleResultValue> = Vec::new();
209 while let Some(task) = tasks.pop() {
210 match task {
211 RuleResultTask::Visit(value) => {
212 let mut children: Vec<&DomainRuleResultValue> = Vec::new();
213 if let Some(range) = &value.range {
214 children.push(&range.from);
215 children.push(&range.to);
216 }
217 let list_len = value.list.as_ref().map(|rows| {
218 children.extend(rows.iter());
219 rows.len()
220 });
221 let field_names = value.fields.as_ref().map(|fields| {
222 children.extend(fields.values());
223 fields.keys().cloned().collect()
224 });
225 let frame = RuleResultFrame {
226 shell: RuleResultValue {
227 result: value.result.clone(),
228 measure: value.measure.clone(),
229 ratio: value.ratio.clone(),
230 number: value.number,
231 boolean: value.boolean,
232 text: value.text.clone(),
233 date: value.date.clone(),
234 time: value.time.clone(),
235 calendar: value.calendar.as_ref().map(CalendarResult::from),
236 unit: value.unit.clone(),
237 range: None,
238 spec: value.spec.clone(),
239 row: value.row.clone(),
240 list: None,
241 fields: None,
242 reference: value.reference.clone(),
243 },
244 has_range: value.range.is_some(),
245 list_len,
246 field_names,
247 child_count: children.len(),
248 };
249 tasks.push(RuleResultTask::Assemble(Box::new(frame)));
250 tasks.extend(children.into_iter().rev().map(RuleResultTask::Visit));
251 }
252 RuleResultTask::Assemble(frame) => {
253 let frame = *frame;
254 let mut children = converted
255 .split_off(converted.len() - frame.child_count)
256 .into_iter();
257 let mut shell = frame.shell;
258 if frame.has_range {
259 let from = children
260 .next()
261 .expect("BUG: range value has a from endpoint");
262 let to = children.next().expect("BUG: range value has a to endpoint");
263 shell.range = Some(Box::new(RangeResult { from, to }));
264 }
265 if let Some(len) = frame.list_len {
266 shell.list = Some(children.by_ref().take(len).collect());
267 }
268 if let Some(names) = frame.field_names {
269 shell.fields = Some(names.into_iter().zip(children).collect());
270 }
271 converted.push(shell);
272 }
273 }
274 }
275 converted
276 .pop()
277 .expect("BUG: rule result value conversion yields exactly one value")
278 }
279}