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 }
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub struct LiteralValue {
68 pub value: ValueKind,
69}
70
71impl From<&DomainLiteralValue> for LiteralValue {
72 fn from(literal: &DomainLiteralValue) -> Self {
73 Self {
74 value: ValueKind::from(&literal.value),
75 }
76 }
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
80pub struct CalendarResult {
81 pub value: Decimal,
82 pub unit: String,
83}
84
85impl From<&DomainCalendarResult> for CalendarResult {
86 fn from(calendar: &DomainCalendarResult) -> Self {
87 Self {
88 value: calendar.value,
89 unit: calendar.unit.clone(),
90 }
91 }
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
95pub struct RangeResult {
96 pub from: RuleResultValue,
97 pub to: RuleResultValue,
98}
99
100impl From<&DomainRangeResult> for RangeResult {
101 fn from(range: &DomainRangeResult) -> Self {
102 Self {
103 from: RuleResultValue::from(&range.from),
104 to: RuleResultValue::from(&range.to),
105 }
106 }
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
110pub struct RuleResultValue {
111 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub result: Option<String>,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub measure: Option<BTreeMap<String, Decimal>>,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub ratio: Option<BTreeMap<String, Decimal>>,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub number: Option<Decimal>,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub boolean: Option<bool>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub text: Option<String>,
123 #[serde(default, skip_serializing_if = "Option::is_none")]
124 pub date: Option<SemanticDateTime>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub time: Option<SemanticTime>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
128 pub calendar: Option<CalendarResult>,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub range: Option<Box<RangeResult>>,
131}
132
133impl From<&DomainRuleResultValue> for RuleResultValue {
134 fn from(value: &DomainRuleResultValue) -> Self {
135 Self {
136 result: value.result.clone(),
137 measure: value.measure.clone(),
138 ratio: value.ratio.clone(),
139 number: value.number,
140 boolean: value.boolean,
141 text: value.text.clone(),
142 date: value.date.clone(),
143 time: value.time.clone(),
144 calendar: value.calendar.as_ref().map(CalendarResult::from),
145 range: value
146 .range
147 .as_ref()
148 .map(|range| Box::new(RangeResult::from(range.as_ref()))),
149 }
150 }
151}