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.to_string()),
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 DomainValueKind::UnscaledMeasure { .. } => {
69 unreachable!("BUG: unscaled measure outside its owning leaf")
70 }
71 }
72 }
73}
74
75enum ValueKindTask<'a> {
76 Visit(&'a DomainValueKind),
77 BuildRange,
78}
79
80impl From<&DomainValueKind> for ValueKind {
81 fn from(value: &DomainValueKind) -> Self {
82 let mut tasks = vec![ValueKindTask::Visit(value)];
83 let mut converted: Vec<ValueKind> = Vec::new();
84 while let Some(task) = tasks.pop() {
85 match task {
86 ValueKindTask::Visit(DomainValueKind::Range(from, to)) => {
87 tasks.push(ValueKindTask::BuildRange);
88 tasks.push(ValueKindTask::Visit(&to.value));
89 tasks.push(ValueKindTask::Visit(&from.value));
90 }
91 ValueKindTask::Visit(other) => converted.push(ValueKind::from_non_range(other)),
92 ValueKindTask::BuildRange => {
93 let to = converted
94 .pop()
95 .expect("BUG: range endpoint converted before build");
96 let from = converted
97 .pop()
98 .expect("BUG: range endpoint converted before build");
99 converted.push(Self::Range {
100 from: Box::new(from),
101 to: Box::new(to),
102 });
103 }
104 }
105 }
106 converted
107 .pop()
108 .expect("BUG: value conversion yields exactly one ValueKind")
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113pub struct LiteralValue {
114 pub value: ValueKind,
115}
116
117impl From<&DomainLiteralValue> for LiteralValue {
118 fn from(literal: &DomainLiteralValue) -> Self {
119 Self {
120 value: ValueKind::from(&literal.value),
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
126pub struct CalendarResult {
127 pub value: Decimal,
128 pub unit: String,
129}
130
131impl From<&DomainCalendarResult> for CalendarResult {
132 fn from(calendar: &DomainCalendarResult) -> Self {
133 Self {
134 value: calendar.value,
135 unit: calendar.unit.clone(),
136 }
137 }
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
141pub struct RangeResult {
142 pub from: RuleResultValue,
143 pub to: RuleResultValue,
144}
145
146impl From<&DomainRangeResult> for RangeResult {
147 fn from(range: &DomainRangeResult) -> Self {
148 Self {
149 from: RuleResultValue::from(&range.from),
150 to: RuleResultValue::from(&range.to),
151 }
152 }
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
156pub struct RuleResultValue {
157 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub result: Option<String>,
159 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub measure: Option<BTreeMap<String, Decimal>>,
161 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub ratio: Option<BTreeMap<String, Decimal>>,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub number: Option<Decimal>,
165 #[serde(default, skip_serializing_if = "Option::is_none")]
166 pub boolean: Option<bool>,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub text: Option<String>,
169 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub date: Option<SemanticDateTime>,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
172 pub time: Option<SemanticTime>,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
174 pub calendar: Option<CalendarResult>,
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub unit: Option<String>,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub range: Option<Box<RangeResult>>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub spec: Option<Box<crate::result_value::SpecResult>>,
181 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub row: Option<crate::result_value::RowReference>,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub list: Option<Vec<RuleResultValue>>,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
186 pub fields: Option<IndexMap<String, RuleResultValue>>,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub reference: Option<String>,
191}
192
193struct RuleResultFrame {
196 shell: RuleResultValue,
197 has_range: bool,
198 list_len: Option<usize>,
199 field_names: Option<Vec<String>>,
200 child_count: usize,
201}
202
203enum RuleResultTask<'a> {
204 Visit(&'a DomainRuleResultValue),
205 Assemble(Box<RuleResultFrame>),
206}
207
208impl From<&DomainRuleResultValue> for RuleResultValue {
209 fn from(value: &DomainRuleResultValue) -> Self {
210 let mut tasks = vec![RuleResultTask::Visit(value)];
211 let mut converted: Vec<RuleResultValue> = Vec::new();
212 while let Some(task) = tasks.pop() {
213 match task {
214 RuleResultTask::Visit(value) => {
215 let mut children: Vec<&DomainRuleResultValue> = Vec::new();
216 if let Some(range) = &value.range {
217 children.push(&range.from);
218 children.push(&range.to);
219 }
220 let list_len = value.list.as_ref().map(|rows| {
221 children.extend(rows.iter());
222 rows.len()
223 });
224 let field_names = value.fields.as_ref().map(|fields| {
225 children.extend(fields.values());
226 fields.keys().cloned().collect()
227 });
228 let frame = RuleResultFrame {
229 shell: RuleResultValue {
230 result: value.result.clone(),
231 measure: value.measure.clone(),
232 ratio: value.ratio.clone(),
233 number: value.number,
234 boolean: value.boolean,
235 text: value.text.clone(),
236 date: value.date.clone(),
237 time: value.time.clone(),
238 calendar: value.calendar.as_ref().map(CalendarResult::from),
239 unit: value.unit.clone(),
240 range: None,
241 spec: value.spec.clone(),
242 row: value.row.clone(),
243 list: None,
244 fields: None,
245 reference: value.reference.clone(),
246 },
247 has_range: value.range.is_some(),
248 list_len,
249 field_names,
250 child_count: children.len(),
251 };
252 tasks.push(RuleResultTask::Assemble(Box::new(frame)));
253 tasks.extend(children.into_iter().rev().map(RuleResultTask::Visit));
254 }
255 RuleResultTask::Assemble(frame) => {
256 let frame = *frame;
257 let mut children = converted
258 .split_off(converted.len() - frame.child_count)
259 .into_iter();
260 let mut shell = frame.shell;
261 if frame.has_range {
262 let from = children
263 .next()
264 .expect("BUG: range value has a from endpoint");
265 let to = children.next().expect("BUG: range value has a to endpoint");
266 shell.range = Some(Box::new(RangeResult { from, to }));
267 }
268 if let Some(len) = frame.list_len {
269 shell.list = Some(children.by_ref().take(len).collect());
270 }
271 if let Some(names) = frame.field_names {
272 shell.fields = Some(names.into_iter().zip(children).collect());
273 }
274 converted.push(shell);
275 }
276 }
277 }
278 converted
279 .pop()
280 .expect("BUG: rule result value conversion yields exactly one value")
281 }
282}