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