lemma/evaluation/
operations.rs1use std::fmt;
4use std::sync::Arc;
5
6use crate::planning::semantics::{
7 ArithmeticComputation, ComparisonComputation, DataPath, LemmaType, LiteralValue,
8 LogicalComputation, MathematicalComputation, SemanticConversionTarget, SemanticDateTime,
9 SemanticTime, TypeSpecification,
10};
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, PartialEq)]
18pub enum VetoType {
19 MissingData { data: DataPath },
21 UserDefined { message: Option<String> },
23 Computation { message: String },
25}
26
27impl fmt::Display for VetoType {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 VetoType::MissingData { data } => write!(f, "Missing data: {}", data),
31 VetoType::UserDefined { message: Some(msg) } => write!(f, "{msg}"),
32 VetoType::UserDefined { message: None } => write!(f, "Vetoed"),
33 VetoType::Computation { message } => write!(f, "{message}"),
34 }
35 }
36}
37
38impl VetoType {
39 #[must_use]
40 pub fn computation(message: impl Into<String>) -> Self {
41 VetoType::Computation {
42 message: message.into(),
43 }
44 }
45}
46
47impl Serialize for VetoType {
48 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
49 where
50 S: serde::Serializer,
51 {
52 serializer.serialize_str(&self.to_string())
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Serialize)]
58#[serde(rename_all = "snake_case")]
59pub enum OperationResult {
60 Value(Arc<LiteralValue>),
62 Veto(VetoType),
64}
65
66impl OperationResult {
67 pub fn from_literal(value: LiteralValue) -> Self {
68 Self::Value(Arc::new(value))
69 }
70
71 pub fn from_literal_arc(value: Arc<LiteralValue>) -> Self {
72 Self::Value(value)
73 }
74
75 pub fn vetoed(&self) -> bool {
76 matches!(self, OperationResult::Veto(_))
77 }
78
79 #[must_use]
80 pub fn value(&self) -> Option<&LiteralValue> {
81 match self {
82 OperationResult::Value(value) => Some(value.as_ref()),
83 OperationResult::Veto(_) => None,
84 }
85 }
86
87 #[must_use]
88 pub fn value_arc(&self) -> Option<&Arc<LiteralValue>> {
89 match self {
90 OperationResult::Value(value) => Some(value),
91 OperationResult::Veto(_) => None,
92 }
93 }
94
95 pub fn number(number: rust_decimal::Decimal) -> Self {
96 Self::from_literal(LiteralValue::number_from_decimal(number))
97 }
98
99 pub fn measure(
100 value: rust_decimal::Decimal,
101 unit: impl Into<String>,
102 lemma_type: Option<LemmaType>,
103 ) -> Self {
104 use crate::computation::rational::checked_mul;
105 let lemma_type = std::sync::Arc::new(
106 lemma_type.unwrap_or_else(|| LemmaType::primitive(TypeSpecification::measure())),
107 );
108 let unit_name = unit.into();
109 let rational = crate::literals::rational_from_parsed_decimal(value)
110 .expect("BUG: operation result measure must lift at boundary");
111 let factor = if let TypeSpecification::Measure { units, .. } = &lemma_type.specifications {
112 units
113 .get(&unit_name)
114 .map(|u| u.factor.clone())
115 .unwrap_or_else(|_| {
116 panic!(
117 "BUG: OperationResult::measure unit '{}' not declared on type",
118 unit_name
119 )
120 })
121 } else {
122 crate::computation::rational::rational_one()
123 };
124 let canonical = checked_mul(&rational, &factor)
125 .expect("BUG: measure canonicalization overflow in OperationResult::measure");
126 Self::from_literal(LiteralValue::measure_with_type(
127 canonical, unit_name, lemma_type,
128 ))
129 }
130
131 pub fn text(text: impl Into<String>) -> Self {
132 Self::from_literal(LiteralValue::text(text.into()))
133 }
134
135 pub fn date(date: impl Into<SemanticDateTime>) -> Self {
136 Self::from_literal(LiteralValue::date(date.into()))
137 }
138
139 pub fn time(time: impl Into<SemanticTime>) -> Self {
140 Self::from_literal(LiteralValue::time(time.into()))
141 }
142
143 pub fn boolean(boolean: bool) -> Self {
144 Self::from_literal(LiteralValue::from_bool(boolean))
145 }
146
147 pub fn ratio(rational: rust_decimal::Decimal) -> Self {
148 Self::from_literal(LiteralValue::ratio_from_decimal(rational, None))
149 }
150
151 pub fn veto(veto: impl Into<String>) -> Self {
152 Self::Veto(VetoType::UserDefined {
153 message: Some(veto.into()),
154 })
155 }
156}
157
158#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160#[serde(tag = "type", content = "computation", rename_all = "snake_case")]
161pub enum ComputationKind {
162 Arithmetic(ArithmeticComputation),
163 Comparison(ComparisonComputation),
164 Mathematical(MathematicalComputation),
165 Logical(LogicalComputation),
166 UnitConversion {
167 target: SemanticConversionTarget,
168 },
169 ResultIsVeto,
171}
172
173#[cfg(test)]
174mod computation_kind_serde_tests {
175 use super::ComputationKind;
176 use crate::parsing::ast::{
177 ArithmeticComputation, ComparisonComputation, MathematicalComputation,
178 };
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_mathematical_round_trip() {
199 let k = ComputationKind::Mathematical(MathematicalComputation::Sqrt);
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_unit_conversion_round_trip() {
207 use crate::parsing::ast::PrimitiveKind;
208 use crate::planning::semantics::SemanticConversionTarget;
209 let k = ComputationKind::UnitConversion {
210 target: SemanticConversionTarget::Type(PrimitiveKind::Number),
211 };
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}