1use std::fmt;
4
5use crate::{
6 planning::semantics::{
7 ArithmeticComputation, ComparisonComputation, DataPath, LiteralValue, LogicalComputation,
8 MathematicalComputation, RulePath, SemanticDateTime, SemanticTime,
9 },
10 LemmaType, SemanticDurationUnit, TypeSpecification,
11};
12use rust_decimal::Decimal;
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, PartialEq)]
20pub enum VetoType {
21 MissingData { data: DataPath },
23 UserDefined { message: Option<String> },
25 Computation { message: String },
27}
28
29impl fmt::Display for VetoType {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 VetoType::MissingData { data } => write!(f, "Missing data: {}", data),
33 VetoType::UserDefined { message: Some(msg) } => write!(f, "{msg}"),
34 VetoType::UserDefined { message: None } => write!(f, "Vetoed"),
35 VetoType::Computation { message } => write!(f, "{message}"),
36 }
37 }
38}
39
40impl VetoType {
41 #[must_use]
42 pub fn computation(message: impl Into<String>) -> Self {
43 VetoType::Computation {
44 message: message.into(),
45 }
46 }
47}
48
49impl Serialize for VetoType {
50 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51 where
52 S: serde::Serializer,
53 {
54 serializer.serialize_str(&self.to_string())
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Serialize)]
60#[serde(rename_all = "snake_case")]
61pub enum OperationResult {
62 Value(Box<LiteralValue>),
64 Veto(VetoType),
66}
67
68impl OperationResult {
69 pub fn vetoed(&self) -> bool {
70 matches!(self, OperationResult::Veto(_))
71 }
72
73 #[must_use]
74 pub fn value(&self) -> Option<&LiteralValue> {
75 match self {
76 OperationResult::Value(v) => Some(v.as_ref()),
77 OperationResult::Veto(_) => None,
78 }
79 }
80
81 pub fn number(number: impl Into<Decimal>) -> Self {
82 Self::Value(Box::new(LiteralValue::number(number.into())))
83 }
84
85 pub fn scale(
86 scale: impl Into<Decimal>,
87 unit: impl Into<String>,
88 lemma_type: Option<LemmaType>,
89 ) -> Self {
90 let lemma_type =
91 lemma_type.unwrap_or_else(|| LemmaType::primitive(TypeSpecification::scale()));
92 Self::Value(Box::new(LiteralValue::scale_with_type(
93 scale.into(),
94 unit.into(),
95 lemma_type,
96 )))
97 }
98
99 pub fn text(text: impl Into<String>) -> Self {
100 Self::Value(Box::new(LiteralValue::text(text.into())))
101 }
102
103 pub fn date(date: impl Into<SemanticDateTime>) -> Self {
104 Self::Value(Box::new(LiteralValue::date(date.into())))
105 }
106
107 pub fn time(time: impl Into<SemanticTime>) -> Self {
108 Self::Value(Box::new(LiteralValue::time(time.into())))
109 }
110
111 pub fn boolean(boolean: bool) -> Self {
112 Self::Value(Box::new(LiteralValue::from_bool(boolean)))
113 }
114
115 pub fn duration(duration: impl Into<Decimal>, unit: impl Into<SemanticDurationUnit>) -> Self {
116 Self::Value(Box::new(LiteralValue::duration(
117 duration.into(),
118 unit.into(),
119 )))
120 }
121
122 pub fn ratio(ratio: impl Into<Decimal>) -> Self {
123 Self::Value(Box::new(LiteralValue::ratio(ratio.into(), None)))
124 }
125
126 pub fn veto(veto: impl Into<String>) -> Self {
127 Self::Veto(VetoType::UserDefined {
128 message: Some(veto.into()),
129 })
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135#[serde(tag = "type", content = "computation", rename_all = "snake_case")]
136pub enum ComputationKind {
137 Arithmetic(ArithmeticComputation),
138 Comparison(ComparisonComputation),
139 Logical(LogicalComputation),
140 Mathematical(MathematicalComputation),
141}
142
143#[derive(Debug, Clone, Serialize)]
145pub struct OperationRecord {
146 #[serde(flatten)]
147 pub kind: OperationKind,
148}
149
150#[derive(Debug, Clone, Serialize)]
152#[serde(tag = "type", rename_all = "snake_case")]
153pub enum OperationKind {
154 DataUsed {
155 data_ref: DataPath,
156 value: LiteralValue,
157 },
158 RuleUsed {
159 rule_path: RulePath,
160 result: OperationResult,
161 },
162 Computation {
163 kind: ComputationKind,
164 inputs: Vec<LiteralValue>,
165 result: LiteralValue,
166 },
167 RuleBranchEvaluated {
168 #[serde(skip_serializing_if = "Option::is_none")]
169 index: Option<usize>,
170 matched: bool,
171 #[serde(skip_serializing_if = "Option::is_none", default)]
172 result_value: Option<OperationResult>,
173 },
174}
175
176#[cfg(test)]
177mod computation_kind_serde_tests {
178 use super::ComputationKind;
179 use crate::parsing::ast::{
180 ArithmeticComputation, ComparisonComputation, MathematicalComputation,
181 };
182 use crate::planning::semantics::LogicalComputation;
183
184 #[test]
185 fn computation_kind_arithmetic_round_trip() {
186 let k = ComputationKind::Arithmetic(ArithmeticComputation::Add);
187 let json = serde_json::to_string(&k).expect("serialize");
188 assert!(json.contains("\"type\"") && json.contains("\"computation\""));
189 let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
190 assert_eq!(back, k);
191 }
192
193 #[test]
194 fn computation_kind_comparison_round_trip() {
195 let k = ComputationKind::Comparison(ComparisonComputation::GreaterThan);
196 let json = serde_json::to_string(&k).expect("serialize");
197 let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
198 assert_eq!(back, k);
199 }
200
201 #[test]
202 fn computation_kind_logical_round_trip() {
203 let k = ComputationKind::Logical(LogicalComputation::And);
204 let json = serde_json::to_string(&k).expect("serialize");
205 let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
206 assert_eq!(back, k);
207 }
208
209 #[test]
210 fn computation_kind_mathematical_round_trip() {
211 let k = ComputationKind::Mathematical(MathematicalComputation::Sqrt);
212 let json = serde_json::to_string(&k).expect("serialize");
213 let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
214 assert_eq!(back, k);
215 }
216
217 #[test]
218 fn veto_type_serializes_as_display_string() {
219 use super::VetoType;
220 use crate::planning::semantics::DataPath;
221 let v = VetoType::MissingData {
222 data: DataPath::new(vec![], "product".to_string()),
223 };
224 let json = serde_json::to_string(&v).expect("serialize");
225 assert_eq!(json, "\"Missing data: product\"");
226 }
227}