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 rust_decimal::Decimal;
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "lowercase")]
18pub enum ValueKind {
19 Number(Decimal),
20 Measure {
21 value: Decimal,
22 },
23 Ratio {
24 value: Decimal,
25 },
26 Text(String),
27 Date(SemanticDateTime),
28 Time(SemanticTime),
29 Boolean(bool),
30 Range {
31 from: Box<ValueKind>,
32 to: Box<ValueKind>,
33 },
34}
35
36impl From<&DomainValueKind> for ValueKind {
37 fn from(value: &DomainValueKind) -> Self {
38 match value {
39 DomainValueKind::Number(rational) => Self::Number(
40 rational
41 .try_to_decimal()
42 .expect("BUG: planned number must convert to decimal"),
43 ),
44 DomainValueKind::Measure(rational) => Self::Measure {
45 value: rational
46 .try_to_decimal()
47 .expect("BUG: planned measure must convert to decimal"),
48 },
49 DomainValueKind::Ratio(rational) => Self::Ratio {
50 value: rational
51 .try_to_decimal()
52 .expect("BUG: planned ratio must convert to decimal"),
53 },
54 DomainValueKind::Text(text) => Self::Text(text.clone()),
55 DomainValueKind::Date(date) => Self::Date(date.clone()),
56 DomainValueKind::Time(time) => Self::Time(time.clone()),
57 DomainValueKind::Boolean(flag) => Self::Boolean(*flag),
58 DomainValueKind::Range(from, to) => Self::Range {
59 from: Box::new(ValueKind::from(&from.value)),
60 to: Box::new(ValueKind::from(&to.value)),
61 },
62 DomainValueKind::Spec(_) => {
63 unreachable!("BUG: spec values are rule results, not nested ValueKind payloads")
64 }
65 }
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70pub struct LiteralValue {
71 pub value: ValueKind,
72}
73
74impl From<&DomainLiteralValue> for LiteralValue {
75 fn from(literal: &DomainLiteralValue) -> Self {
76 Self {
77 value: ValueKind::from(&literal.value),
78 }
79 }
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
83pub struct CalendarResult {
84 pub value: Decimal,
85 pub unit: String,
86}
87
88impl From<&DomainCalendarResult> for CalendarResult {
89 fn from(calendar: &DomainCalendarResult) -> Self {
90 Self {
91 value: calendar.value,
92 unit: calendar.unit.clone(),
93 }
94 }
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
98pub struct RangeResult {
99 pub from: RuleResultValue,
100 pub to: RuleResultValue,
101}
102
103impl From<&DomainRangeResult> for RangeResult {
104 fn from(range: &DomainRangeResult) -> Self {
105 Self {
106 from: RuleResultValue::from(&range.from),
107 to: RuleResultValue::from(&range.to),
108 }
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
113pub struct RuleResultValue {
114 #[serde(default, skip_serializing_if = "Option::is_none")]
115 pub result: Option<String>,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub measure: Option<BTreeMap<String, Decimal>>,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub ratio: Option<BTreeMap<String, Decimal>>,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub number: Option<Decimal>,
122 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub boolean: Option<bool>,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub text: Option<String>,
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub date: Option<SemanticDateTime>,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub time: Option<SemanticTime>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub calendar: Option<CalendarResult>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub unit: Option<String>,
134 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub range: Option<Box<RangeResult>>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub spec: Option<Box<crate::result_value::SpecResult>>,
138}
139
140impl From<&DomainRuleResultValue> for RuleResultValue {
141 fn from(value: &DomainRuleResultValue) -> Self {
142 Self {
143 result: value.result.clone(),
144 measure: value.measure.clone(),
145 ratio: value.ratio.clone(),
146 number: value.number,
147 boolean: value.boolean,
148 text: value.text.clone(),
149 date: value.date.clone(),
150 time: value.time.clone(),
151 calendar: value.calendar.as_ref().map(CalendarResult::from),
152 unit: value.unit.clone(),
153 range: value
154 .range
155 .as_ref()
156 .map(|range| Box::new(RangeResult::from(range.as_ref()))),
157 spec: value.spec.clone(),
158 }
159 }
160}