1use crate::ast::{ExpressionId, Span};
2use rust_decimal::Decimal;
3use serde::Serialize;
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct LemmaDoc {
9 pub name: String,
10 pub source: Option<String>,
11 pub start_line: usize,
12 pub commentary: Option<String>,
13 pub facts: Vec<LemmaFact>,
14 pub rules: Vec<LemmaRule>,
15}
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct LemmaFact {
19 pub fact_type: FactType,
20 pub value: FactValue,
21 pub span: Option<Span>,
22}
23
24#[derive(Debug, Clone, PartialEq)]
25pub enum FactType {
26 Local(String),
27 Foreign(ForeignFact),
28}
29
30#[derive(Debug, Clone, PartialEq)]
32pub struct ForeignFact {
33 pub reference: Vec<String>,
34}
35
36#[derive(Debug, Clone, PartialEq)]
42pub struct UnlessClause {
43 pub condition: Expression,
44 pub result: Expression,
45 pub span: Option<Span>,
46}
47
48#[derive(Debug, Clone, PartialEq)]
50pub struct LemmaRule {
51 pub name: String,
52 pub expression: Expression,
53 pub unless_clauses: Vec<UnlessClause>,
54 pub span: Option<Span>,
55}
56
57#[derive(Debug, Clone, PartialEq)]
59pub struct Expression {
60 pub kind: ExpressionKind,
61 pub span: Option<Span>,
62 pub id: ExpressionId,
63}
64
65impl Expression {
66 pub fn new(kind: ExpressionKind, span: Option<Span>, id: ExpressionId) -> Self {
68 Self { kind, span, id }
69 }
70}
71
72#[derive(Debug, Clone, PartialEq)]
74pub enum ExpressionKind {
75 Literal(LiteralValue),
76 FactReference(FactReference),
77 RuleReference(RuleReference),
78 LogicalAnd(Box<Expression>, Box<Expression>),
79 LogicalOr(Box<Expression>, Box<Expression>),
80 Arithmetic(Box<Expression>, ArithmeticOperation, Box<Expression>),
81 Comparison(Box<Expression>, ComparisonOperator, Box<Expression>),
82 FactHasAnyValue(FactReference),
83 UnitConversion(Box<Expression>, ConversionTarget),
84 LogicalNegation(Box<Expression>, NegationType),
85 MathematicalOperator(MathematicalOperator, Box<Expression>),
86 Veto(VetoExpression),
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Hash)]
91pub struct FactReference {
92 pub reference: Vec<String>, }
94
95#[derive(Debug, Clone, PartialEq, Eq, Hash)]
102pub struct RuleReference {
103 pub reference: Vec<String>, }
105
106#[derive(Debug, Clone, PartialEq, Eq, Hash)]
108pub enum ArithmeticOperation {
109 Add,
110 Subtract,
111 Multiply,
112 Divide,
113 Modulo,
114 Power,
115}
116
117impl ArithmeticOperation {
118 pub fn name(&self) -> &'static str {
120 match self {
121 ArithmeticOperation::Add => "addition",
122 ArithmeticOperation::Subtract => "subtraction",
123 ArithmeticOperation::Multiply => "multiplication",
124 ArithmeticOperation::Divide => "division",
125 ArithmeticOperation::Modulo => "modulo",
126 ArithmeticOperation::Power => "exponentiation",
127 }
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Hash)]
133pub enum ComparisonOperator {
134 GreaterThan,
135 LessThan,
136 GreaterThanOrEqual,
137 LessThanOrEqual,
138 Equal,
139 NotEqual,
140 Is,
141 IsNot,
142}
143
144impl ComparisonOperator {
145 pub fn name(&self) -> &'static str {
147 match self {
148 ComparisonOperator::GreaterThan => "greater than",
149 ComparisonOperator::LessThan => "less than",
150 ComparisonOperator::GreaterThanOrEqual => "greater than or equal",
151 ComparisonOperator::LessThanOrEqual => "less than or equal",
152 ComparisonOperator::Equal => "equal",
153 ComparisonOperator::NotEqual => "not equal",
154 ComparisonOperator::Is => "is",
155 ComparisonOperator::IsNot => "is not",
156 }
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, Hash)]
162pub enum ConversionTarget {
163 Mass(MassUnit),
164 Length(LengthUnit),
165 Volume(VolumeUnit),
166 Duration(DurationUnit),
167 Temperature(TemperatureUnit),
168 Power(PowerUnit),
169 Force(ForceUnit),
170 Pressure(PressureUnit),
171 Energy(EnergyUnit),
172 Frequency(FrequencyUnit),
173 Data(DataUnit),
174 Money(MoneyUnit),
175 Percentage,
176}
177
178#[derive(Debug, Clone, PartialEq, Eq, Hash)]
180pub enum NegationType {
181 Not, HaveNot, NotHave, }
185
186#[derive(Debug, Clone, PartialEq)]
194pub struct VetoExpression {
195 pub message: Option<String>,
196}
197
198#[derive(Debug, Clone, PartialEq, Eq, Hash)]
200pub enum MathematicalOperator {
201 Sqrt, Sin, Cos, Tan, Asin, Acos, Atan, Log, Exp, Abs, Floor, Ceil, Round, }
215
216#[derive(Debug, Clone, PartialEq)]
217pub enum FactValue {
218 Literal(LiteralValue),
219 DocumentReference(String),
220 TypeAnnotation(TypeAnnotation),
221}
222
223#[derive(Debug, Clone, PartialEq)]
224pub enum TypeAnnotation {
225 LemmaType(LemmaType),
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Hash)]
230pub enum LemmaType {
231 Text,
232 Number,
233 Date,
234 Boolean,
235 Regex,
236 Percentage,
237 Mass,
238 Length,
239 Volume,
240 Duration,
241 Temperature,
242 Power,
243 Energy,
244 Force,
245 Pressure,
246 Frequency,
247 Data,
248 Money,
249}
250
251#[derive(Debug, Clone, PartialEq, Serialize)]
253pub enum LiteralValue {
254 Number(Decimal),
255 Text(String),
256 Date(DateTimeValue), Time(TimeValue), Boolean(bool),
259 Percentage(Decimal),
260 Unit(NumericUnit), Regex(String), }
263
264impl LiteralValue {
265 pub fn display_value(&self) -> String {
267 self.to_string()
268 }
269
270 pub fn byte_size(&self) -> usize {
272 match self {
273 LiteralValue::Text(s) | LiteralValue::Regex(s) => s.len(),
274 LiteralValue::Number(d) | LiteralValue::Percentage(d) => {
275 std::mem::size_of_val(d)
277 }
278 LiteralValue::Boolean(_) => std::mem::size_of::<bool>(),
279 LiteralValue::Date(_) => std::mem::size_of::<DateTimeValue>(),
280 LiteralValue::Time(_) => std::mem::size_of::<TimeValue>(),
281 LiteralValue::Unit(_) => std::mem::size_of::<NumericUnit>(),
282 }
283 }
284
285 pub fn to_type(&self) -> LemmaType {
287 match self {
288 LiteralValue::Text(_) => LemmaType::Text,
289 LiteralValue::Number(_) => LemmaType::Number,
290 LiteralValue::Date(_) => LemmaType::Date,
291 LiteralValue::Time(_) => LemmaType::Date,
292 LiteralValue::Boolean(_) => LemmaType::Boolean,
293 LiteralValue::Percentage(_) => LemmaType::Percentage,
294 LiteralValue::Regex(_) => LemmaType::Regex,
295 LiteralValue::Unit(unit) => match unit {
296 NumericUnit::Mass(_, _) => LemmaType::Mass,
297 NumericUnit::Length(_, _) => LemmaType::Length,
298 NumericUnit::Volume(_, _) => LemmaType::Volume,
299 NumericUnit::Duration(_, _) => LemmaType::Duration,
300 NumericUnit::Temperature(_, _) => LemmaType::Temperature,
301 NumericUnit::Power(_, _) => LemmaType::Power,
302 NumericUnit::Force(_, _) => LemmaType::Force,
303 NumericUnit::Pressure(_, _) => LemmaType::Pressure,
304 NumericUnit::Energy(_, _) => LemmaType::Energy,
305 NumericUnit::Frequency(_, _) => LemmaType::Frequency,
306 NumericUnit::Data(_, _) => LemmaType::Data,
307 NumericUnit::Money(_, _) => LemmaType::Money,
308 },
309 }
310 }
311}
312
313#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
315pub struct TimeValue {
316 pub hour: u8,
317 pub minute: u8,
318 pub second: u8,
319 pub timezone: Option<TimezoneValue>,
320}
321
322#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
324pub struct TimezoneValue {
325 pub offset_hours: i8,
326 pub offset_minutes: u8,
327}
328
329#[derive(Debug, Clone, PartialEq, Serialize)]
331pub struct DateTimeValue {
332 pub year: i32,
333 pub month: u32,
334 pub day: u32,
335 pub hour: u32,
336 pub minute: u32,
337 pub second: u32,
338 pub timezone: Option<TimezoneValue>,
339}
340
341macro_rules! impl_unit_serialize {
343 ($($unit_type:ty),+) => {
344 $(
345 impl Serialize for $unit_type {
346 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
347 where
348 S: serde::Serializer,
349 {
350 serializer.serialize_str(&self.to_string())
351 }
352 }
353 )+
354 };
355}
356
357impl_unit_serialize!(
358 MassUnit,
359 LengthUnit,
360 VolumeUnit,
361 DurationUnit,
362 TemperatureUnit,
363 PowerUnit,
364 ForceUnit,
365 PressureUnit,
366 EnergyUnit,
367 FrequencyUnit,
368 DataUnit,
369 MoneyUnit
370);
371
372#[derive(Debug, Clone, PartialEq, Eq, Hash)]
373pub enum MassUnit {
374 Kilogram,
375 Gram,
376 Milligram,
377 Ton,
378 Pound,
379 Ounce,
380}
381
382#[derive(Debug, Clone, PartialEq, Eq, Hash)]
383pub enum LengthUnit {
384 Kilometer,
385 Mile,
386 NauticalMile,
387 Meter,
388 Decimeter,
389 Centimeter,
390 Millimeter,
391 Yard,
392 Foot,
393 Inch,
394}
395
396#[derive(Debug, Clone, PartialEq, Eq, Hash)]
397pub enum VolumeUnit {
398 CubicMeter,
399 CubicCentimeter,
400 Liter,
401 Deciliter,
402 Centiliter,
403 Milliliter,
404 Gallon,
405 Quart,
406 Pint,
407 FluidOunce,
408}
409
410#[derive(Debug, Clone, PartialEq, Eq, Hash)]
411pub enum DurationUnit {
412 Year,
413 Month,
414 Week,
415 Day,
416 Hour,
417 Minute,
418 Second,
419 Millisecond,
420 Microsecond,
421}
422
423#[derive(Debug, Clone, PartialEq, Eq, Hash)]
424pub enum TemperatureUnit {
425 Celsius,
426 Fahrenheit,
427 Kelvin,
428}
429
430#[derive(Debug, Clone, PartialEq, Eq, Hash)]
431pub enum PowerUnit {
432 Megawatt,
433 Kilowatt,
434 Watt,
435 Milliwatt,
436 Horsepower,
437}
438
439#[derive(Debug, Clone, PartialEq, Eq, Hash)]
440pub enum ForceUnit {
441 Newton,
442 Kilonewton,
443 Lbf,
444}
445
446#[derive(Debug, Clone, PartialEq, Eq, Hash)]
447pub enum PressureUnit {
448 Megapascal,
449 Kilopascal,
450 Pascal,
451 Atmosphere,
452 Bar,
453 Psi,
454 Torr,
455 Mmhg,
456}
457
458#[derive(Debug, Clone, PartialEq, Eq, Hash)]
459pub enum EnergyUnit {
460 Megajoule,
461 Kilojoule,
462 Joule,
463 Kilowatthour,
464 Watthour,
465 Kilocalorie,
466 Calorie,
467 Btu,
468}
469
470#[derive(Debug, Clone, PartialEq, Eq, Hash)]
471pub enum FrequencyUnit {
472 Hertz,
473 Kilohertz,
474 Megahertz,
475 Gigahertz,
476}
477
478#[derive(Debug, Clone, PartialEq, Eq, Hash)]
479pub enum DataUnit {
480 Petabyte,
481 Terabyte,
482 Gigabyte,
483 Megabyte,
484 Kilobyte,
485 Byte,
486 Tebibyte,
487 Gibibyte,
488 Mebibyte,
489 Kibibyte,
490}
491
492#[derive(Debug, Clone, PartialEq, Eq, Hash)]
493pub enum MoneyUnit {
494 Eur,
495 Usd,
496 Gbp,
497 Jpy,
498 Cny,
499 Chf,
500 Cad,
501 Aud,
502 Inr,
503}
504
505#[derive(Debug, Clone, PartialEq, Serialize)]
512pub enum NumericUnit {
513 Mass(Decimal, MassUnit),
514 Length(Decimal, LengthUnit),
515 Volume(Decimal, VolumeUnit),
516 Duration(Decimal, DurationUnit),
517 Temperature(Decimal, TemperatureUnit),
518 Power(Decimal, PowerUnit),
519 Force(Decimal, ForceUnit),
520 Pressure(Decimal, PressureUnit),
521 Energy(Decimal, EnergyUnit),
522 Frequency(Decimal, FrequencyUnit),
523 Data(Decimal, DataUnit),
524 Money(Decimal, MoneyUnit),
525}
526
527impl NumericUnit {
528 pub fn value(&self) -> Decimal {
530 match self {
531 NumericUnit::Mass(v, _)
532 | NumericUnit::Length(v, _)
533 | NumericUnit::Volume(v, _)
534 | NumericUnit::Duration(v, _)
535 | NumericUnit::Temperature(v, _)
536 | NumericUnit::Power(v, _)
537 | NumericUnit::Force(v, _)
538 | NumericUnit::Pressure(v, _)
539 | NumericUnit::Energy(v, _)
540 | NumericUnit::Frequency(v, _)
541 | NumericUnit::Data(v, _)
542 | NumericUnit::Money(v, _) => *v,
543 }
544 }
545
546 pub fn same_category(&self, other: &NumericUnit) -> bool {
548 std::mem::discriminant(self) == std::mem::discriminant(other)
549 }
550
551 pub fn with_value(&self, new_value: Decimal) -> NumericUnit {
554 match self {
555 NumericUnit::Mass(_, u) => NumericUnit::Mass(new_value, u.clone()),
556 NumericUnit::Length(_, u) => NumericUnit::Length(new_value, u.clone()),
557 NumericUnit::Volume(_, u) => NumericUnit::Volume(new_value, u.clone()),
558 NumericUnit::Duration(_, u) => NumericUnit::Duration(new_value, u.clone()),
559 NumericUnit::Temperature(_, u) => NumericUnit::Temperature(new_value, u.clone()),
560 NumericUnit::Power(_, u) => NumericUnit::Power(new_value, u.clone()),
561 NumericUnit::Force(_, u) => NumericUnit::Force(new_value, u.clone()),
562 NumericUnit::Pressure(_, u) => NumericUnit::Pressure(new_value, u.clone()),
563 NumericUnit::Energy(_, u) => NumericUnit::Energy(new_value, u.clone()),
564 NumericUnit::Frequency(_, u) => NumericUnit::Frequency(new_value, u.clone()),
565 NumericUnit::Data(_, u) => NumericUnit::Data(new_value, u.clone()),
566 NumericUnit::Money(_, u) => NumericUnit::Money(new_value, u.clone()),
567 }
568 }
569
570 pub fn validate_same_currency(&self, other: &NumericUnit) -> Result<(), crate::LemmaError> {
573 if let (NumericUnit::Money(_, l_curr), NumericUnit::Money(_, r_curr)) = (self, other) {
574 if l_curr != r_curr {
575 return Err(crate::LemmaError::Engine(format!(
576 "Cannot operate on different currencies: {:?} and {:?}",
577 l_curr, r_curr
578 )));
579 }
580 }
581 Ok(())
582 }
583}
584
585impl fmt::Display for NumericUnit {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 match self {
588 NumericUnit::Mass(v, u) => write!(f, "{} {}", v, u),
589 NumericUnit::Length(v, u) => write!(f, "{} {}", v, u),
590 NumericUnit::Volume(v, u) => write!(f, "{} {}", v, u),
591 NumericUnit::Duration(v, u) => write!(f, "{} {}", v, u),
592 NumericUnit::Temperature(v, u) => write!(f, "{} {}", v, u),
593 NumericUnit::Power(v, u) => write!(f, "{} {}", v, u),
594 NumericUnit::Force(v, u) => write!(f, "{} {}", v, u),
595 NumericUnit::Pressure(v, u) => write!(f, "{} {}", v, u),
596 NumericUnit::Energy(v, u) => write!(f, "{} {}", v, u),
597 NumericUnit::Frequency(v, u) => write!(f, "{} {}", v, u),
598 NumericUnit::Data(v, u) => write!(f, "{} {}", v, u),
599 NumericUnit::Money(v, u) => write!(f, "{} {}", v, u),
600 }
601 }
602}
603
604impl LemmaRule {
605 pub fn new(name: String, expression: Expression) -> Self {
606 Self {
607 name,
608 expression,
609 unless_clauses: Vec::new(),
610 span: None,
611 }
612 }
613
614 pub fn add_unless_clause(mut self, unless_clause: UnlessClause) -> Self {
615 self.unless_clauses.push(unless_clause);
616 self
617 }
618}
619
620impl LemmaFact {
621 pub fn new(fact_type: FactType, value: FactValue) -> Self {
622 Self {
623 fact_type,
624 value,
625 span: None,
626 }
627 }
628
629 pub fn with_span(mut self, span: Span) -> Self {
630 self.span = Some(span);
631 self
632 }
633}
634
635impl LemmaDoc {
636 pub fn new(name: String) -> Self {
637 Self {
638 name,
639 source: None,
640 start_line: 1,
641 commentary: None,
642 facts: Vec::new(),
643 rules: Vec::new(),
644 }
645 }
646
647 pub fn with_source(mut self, source: String) -> Self {
648 self.source = Some(source);
649 self
650 }
651
652 pub fn with_start_line(mut self, start_line: usize) -> Self {
653 self.start_line = start_line;
654 self
655 }
656
657 pub fn set_commentary(mut self, commentary: String) -> Self {
658 self.commentary = Some(commentary);
659 self
660 }
661
662 pub fn add_fact(mut self, fact: LemmaFact) -> Self {
663 self.facts.push(fact);
664 self
665 }
666
667 pub fn add_rule(mut self, rule: LemmaRule) -> Self {
668 self.rules.push(rule);
669 self
670 }
671
672 pub fn get_fact_type(&self, fact_ref: &FactReference) -> Option<LemmaType> {
675 self.facts
676 .iter()
677 .find(|fact| match &fact.fact_type {
678 FactType::Local(name) => {
679 fact_ref.reference.len() == 1 && fact_ref.reference[0] == *name
680 }
681 FactType::Foreign(foreign_ref) => fact_ref.reference == foreign_ref.reference,
682 })
683 .and_then(|fact| match &fact.value {
684 FactValue::Literal(lit) => Some(lit.to_type()),
685 FactValue::TypeAnnotation(TypeAnnotation::LemmaType(lemma_type)) => {
686 Some(lemma_type.clone())
687 }
688 FactValue::DocumentReference(_) => {
689 None
692 }
693 })
694 }
695}
696
697impl fmt::Display for LemmaDoc {
698 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699 write!(f, "doc {}", self.name)?;
700 writeln!(f)?;
701
702 if let Some(ref commentary) = self.commentary {
703 writeln!(f, "\"\"\"{}", commentary)?;
704 writeln!(f, "\"\"\"")?;
705 }
706
707 for fact in &self.facts {
708 write!(f, "{}", fact)?;
709 }
710
711 for rule in &self.rules {
712 write!(f, "{}", rule)?;
713 }
714
715 Ok(())
716 }
717}
718
719impl fmt::Display for LemmaFact {
720 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
721 writeln!(f, "fact {} = {}", self.fact_type, self.value)
722 }
723}
724
725impl fmt::Display for LemmaRule {
726 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
727 write!(f, "rule {} = {}", self.name, self.expression)?;
728
729 for unless_clause in &self.unless_clauses {
730 write!(
731 f,
732 " unless {} then {}",
733 unless_clause.condition, unless_clause.result
734 )?;
735 }
736
737 writeln!(f)?;
738 Ok(())
739 }
740}
741
742impl fmt::Display for Expression {
743 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
744 match &self.kind {
745 ExpressionKind::Literal(lit) => write!(f, "{}", lit),
746 ExpressionKind::FactReference(fact_ref) => write!(f, "{}", fact_ref),
747 ExpressionKind::RuleReference(rule_ref) => write!(f, "{}", rule_ref),
748 ExpressionKind::Arithmetic(left, op, right) => {
749 write!(f, "{} {} {}", left, op, right)
750 }
751 ExpressionKind::Comparison(left, op, right) => {
752 write!(f, "{} {} {}", left, op, right)
753 }
754 ExpressionKind::FactHasAnyValue(fact_ref) => {
755 write!(f, "have {}", fact_ref)
756 }
757 ExpressionKind::UnitConversion(value, target) => {
758 write!(f, "{} in {}", value, target)
759 }
760 ExpressionKind::LogicalNegation(expr, negation_type) => {
761 let prefix = match negation_type {
762 NegationType::Not => "not",
763 NegationType::HaveNot => "have not",
764 NegationType::NotHave => "not have",
765 };
766 write!(f, "{} {}", prefix, expr)
767 }
768 ExpressionKind::LogicalAnd(left, right) => {
769 write!(f, "{} and {}", left, right)
770 }
771 ExpressionKind::LogicalOr(left, right) => {
772 write!(f, "{} or {}", left, right)
773 }
774 ExpressionKind::MathematicalOperator(op, operand) => {
775 let op_name = match op {
776 MathematicalOperator::Sqrt => "sqrt",
777 MathematicalOperator::Sin => "sin",
778 MathematicalOperator::Cos => "cos",
779 MathematicalOperator::Tan => "tan",
780 MathematicalOperator::Asin => "asin",
781 MathematicalOperator::Acos => "acos",
782 MathematicalOperator::Atan => "atan",
783 MathematicalOperator::Log => "log",
784 MathematicalOperator::Exp => "exp",
785 MathematicalOperator::Abs => "abs",
786 MathematicalOperator::Floor => "floor",
787 MathematicalOperator::Ceil => "ceil",
788 MathematicalOperator::Round => "round",
789 };
790 write!(f, "{} {}", op_name, operand)
791 }
792 ExpressionKind::Veto(veto) => match &veto.message {
793 Some(msg) => write!(f, "veto \"{}\"", msg),
794 None => write!(f, "veto"),
795 },
796 }
797 }
798}
799
800impl fmt::Display for LiteralValue {
801 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802 match self {
803 LiteralValue::Number(n) => write!(f, "{}", n),
804 LiteralValue::Text(s) => write!(f, "\"{}\"", s),
805 LiteralValue::Date(dt) => write!(f, "{}", dt),
806 LiteralValue::Boolean(b) => write!(f, "{}", b),
807 LiteralValue::Percentage(p) => write!(f, "{}%", p),
808 LiteralValue::Unit(unit) => write!(f, "{}", unit),
809 LiteralValue::Regex(s) => write!(f, "{}", s),
810 LiteralValue::Time(time) => {
811 write!(f, "time({}, {}, {})", time.hour, time.minute, time.second)
812 }
813 }
814 }
815}
816
817impl LiteralValue {
818 pub fn describe(&self) -> String {
820 match self {
821 LiteralValue::Text(s) => format!("text value \"{}\"", s),
822 LiteralValue::Number(n) => format!("number {}", n),
823 LiteralValue::Boolean(b) => format!("boolean {}", b),
824 LiteralValue::Percentage(p) => format!("percentage {}%", p),
825 LiteralValue::Date(_) => "date value".to_string(),
826 LiteralValue::Unit(unit) => {
827 format!(
828 "{} value {}",
829 LiteralValue::Unit(unit.clone()).to_type(),
830 unit
831 )
832 }
833 LiteralValue::Regex(s) => format!("regex value {}", s),
834 LiteralValue::Time(time) => {
835 format!("time value {}:{}:{}", time.hour, time.minute, time.second)
836 }
837 }
838 }
839}
840
841impl fmt::Display for MassUnit {
842 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
843 match self {
844 MassUnit::Kilogram => write!(f, "kilogram"),
845 MassUnit::Gram => write!(f, "gram"),
846 MassUnit::Milligram => write!(f, "milligram"),
847 MassUnit::Ton => write!(f, "ton"),
848 MassUnit::Pound => write!(f, "pound"),
849 MassUnit::Ounce => write!(f, "ounce"),
850 }
851 }
852}
853
854impl fmt::Display for LengthUnit {
855 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
856 match self {
857 LengthUnit::Kilometer => write!(f, "kilometer"),
858 LengthUnit::Mile => write!(f, "mile"),
859 LengthUnit::NauticalMile => write!(f, "nautical_mile"),
860 LengthUnit::Meter => write!(f, "meter"),
861 LengthUnit::Decimeter => write!(f, "decimeter"),
862 LengthUnit::Centimeter => write!(f, "centimeter"),
863 LengthUnit::Millimeter => write!(f, "millimeter"),
864 LengthUnit::Yard => write!(f, "yard"),
865 LengthUnit::Foot => write!(f, "foot"),
866 LengthUnit::Inch => write!(f, "inch"),
867 }
868 }
869}
870
871impl fmt::Display for VolumeUnit {
872 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
873 match self {
874 VolumeUnit::CubicMeter => write!(f, "cubic_meter"),
875 VolumeUnit::CubicCentimeter => write!(f, "cubic_centimeter"),
876 VolumeUnit::Liter => write!(f, "liter"),
877 VolumeUnit::Deciliter => write!(f, "deciliter"),
878 VolumeUnit::Centiliter => write!(f, "centiliter"),
879 VolumeUnit::Milliliter => write!(f, "milliliter"),
880 VolumeUnit::Gallon => write!(f, "gallon"),
881 VolumeUnit::Quart => write!(f, "quart"),
882 VolumeUnit::Pint => write!(f, "pint"),
883 VolumeUnit::FluidOunce => write!(f, "fluid_ounce"),
884 }
885 }
886}
887
888impl fmt::Display for DurationUnit {
889 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
890 match self {
891 DurationUnit::Year => write!(f, "year"),
892 DurationUnit::Month => write!(f, "month"),
893 DurationUnit::Week => write!(f, "week"),
894 DurationUnit::Day => write!(f, "day"),
895 DurationUnit::Hour => write!(f, "hour"),
896 DurationUnit::Minute => write!(f, "minute"),
897 DurationUnit::Second => write!(f, "second"),
898 DurationUnit::Millisecond => write!(f, "millisecond"),
899 DurationUnit::Microsecond => write!(f, "microsecond"),
900 }
901 }
902}
903
904impl fmt::Display for TemperatureUnit {
905 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
906 match self {
907 TemperatureUnit::Celsius => write!(f, "celsius"),
908 TemperatureUnit::Fahrenheit => write!(f, "fahrenheit"),
909 TemperatureUnit::Kelvin => write!(f, "kelvin"),
910 }
911 }
912}
913
914impl fmt::Display for PowerUnit {
915 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
916 match self {
917 PowerUnit::Megawatt => write!(f, "megawatt"),
918 PowerUnit::Kilowatt => write!(f, "kilowatt"),
919 PowerUnit::Watt => write!(f, "watt"),
920 PowerUnit::Milliwatt => write!(f, "milliwatt"),
921 PowerUnit::Horsepower => write!(f, "horsepower"),
922 }
923 }
924}
925
926impl fmt::Display for ForceUnit {
927 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
928 match self {
929 ForceUnit::Newton => write!(f, "newton"),
930 ForceUnit::Kilonewton => write!(f, "kilonewton"),
931 ForceUnit::Lbf => write!(f, "lbf"),
932 }
933 }
934}
935
936impl fmt::Display for PressureUnit {
937 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
938 match self {
939 PressureUnit::Megapascal => write!(f, "megapascal"),
940 PressureUnit::Kilopascal => write!(f, "kilopascal"),
941 PressureUnit::Pascal => write!(f, "pascal"),
942 PressureUnit::Atmosphere => write!(f, "atmosphere"),
943 PressureUnit::Bar => write!(f, "bar"),
944 PressureUnit::Psi => write!(f, "psi"),
945 PressureUnit::Torr => write!(f, "torr"),
946 PressureUnit::Mmhg => write!(f, "mmhg"),
947 }
948 }
949}
950
951impl fmt::Display for EnergyUnit {
952 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
953 match self {
954 EnergyUnit::Megajoule => write!(f, "megajoule"),
955 EnergyUnit::Kilojoule => write!(f, "kilojoule"),
956 EnergyUnit::Joule => write!(f, "joule"),
957 EnergyUnit::Kilowatthour => write!(f, "kilowatthour"),
958 EnergyUnit::Watthour => write!(f, "watthour"),
959 EnergyUnit::Kilocalorie => write!(f, "kilocalorie"),
960 EnergyUnit::Calorie => write!(f, "calorie"),
961 EnergyUnit::Btu => write!(f, "btu"),
962 }
963 }
964}
965
966impl fmt::Display for FrequencyUnit {
967 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
968 match self {
969 FrequencyUnit::Hertz => write!(f, "hertz"),
970 FrequencyUnit::Kilohertz => write!(f, "kilohertz"),
971 FrequencyUnit::Megahertz => write!(f, "megahertz"),
972 FrequencyUnit::Gigahertz => write!(f, "gigahertz"),
973 }
974 }
975}
976
977impl fmt::Display for DataUnit {
978 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
979 match self {
980 DataUnit::Petabyte => write!(f, "petabyte"),
981 DataUnit::Terabyte => write!(f, "terabyte"),
982 DataUnit::Gigabyte => write!(f, "gigabyte"),
983 DataUnit::Megabyte => write!(f, "megabyte"),
984 DataUnit::Kilobyte => write!(f, "kilobyte"),
985 DataUnit::Byte => write!(f, "byte"),
986 DataUnit::Tebibyte => write!(f, "tebibyte"),
987 DataUnit::Gibibyte => write!(f, "gibibyte"),
988 DataUnit::Mebibyte => write!(f, "mebibyte"),
989 DataUnit::Kibibyte => write!(f, "kibibyte"),
990 }
991 }
992}
993
994impl fmt::Display for MoneyUnit {
995 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
996 match self {
997 MoneyUnit::Eur => write!(f, "EUR"),
998 MoneyUnit::Usd => write!(f, "USD"),
999 MoneyUnit::Gbp => write!(f, "GBP"),
1000 MoneyUnit::Jpy => write!(f, "JPY"),
1001 MoneyUnit::Cny => write!(f, "CNY"),
1002 MoneyUnit::Chf => write!(f, "CHF"),
1003 MoneyUnit::Cad => write!(f, "CAD"),
1004 MoneyUnit::Aud => write!(f, "AUD"),
1005 MoneyUnit::Inr => write!(f, "INR"),
1006 }
1007 }
1008}
1009
1010impl fmt::Display for ConversionTarget {
1011 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1012 match self {
1013 ConversionTarget::Mass(unit) => write!(f, "{}", unit),
1014 ConversionTarget::Length(unit) => write!(f, "{}", unit),
1015 ConversionTarget::Volume(unit) => write!(f, "{}", unit),
1016 ConversionTarget::Duration(unit) => write!(f, "{}", unit),
1017 ConversionTarget::Temperature(unit) => write!(f, "{}", unit),
1018 ConversionTarget::Power(unit) => write!(f, "{}", unit),
1019 ConversionTarget::Force(unit) => write!(f, "{}", unit),
1020 ConversionTarget::Pressure(unit) => write!(f, "{}", unit),
1021 ConversionTarget::Energy(unit) => write!(f, "{}", unit),
1022 ConversionTarget::Frequency(unit) => write!(f, "{}", unit),
1023 ConversionTarget::Data(unit) => write!(f, "{}", unit),
1024 ConversionTarget::Money(unit) => write!(f, "{}", unit),
1025 ConversionTarget::Percentage => write!(f, "percentage"),
1026 }
1027 }
1028}
1029
1030impl fmt::Display for LemmaType {
1031 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1032 match self {
1033 LemmaType::Text => write!(f, "text"),
1034 LemmaType::Number => write!(f, "number"),
1035 LemmaType::Date => write!(f, "date"),
1036 LemmaType::Boolean => write!(f, "boolean"),
1037 LemmaType::Regex => write!(f, "regex"),
1038 LemmaType::Percentage => write!(f, "percentage"),
1039 LemmaType::Mass => write!(f, "mass"),
1040 LemmaType::Length => write!(f, "length"),
1041 LemmaType::Volume => write!(f, "volume"),
1042 LemmaType::Duration => write!(f, "duration"),
1043 LemmaType::Temperature => write!(f, "temperature"),
1044 LemmaType::Power => write!(f, "power"),
1045 LemmaType::Force => write!(f, "force"),
1046 LemmaType::Pressure => write!(f, "pressure"),
1047 LemmaType::Energy => write!(f, "energy"),
1048 LemmaType::Frequency => write!(f, "frequency"),
1049 LemmaType::Data => write!(f, "data"),
1050 LemmaType::Money => write!(f, "money"),
1051 }
1052 }
1053}
1054
1055impl fmt::Display for TypeAnnotation {
1056 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1057 match self {
1058 TypeAnnotation::LemmaType(lemma_type) => write!(f, "{}", lemma_type),
1059 }
1060 }
1061}
1062
1063impl LemmaType {
1064 pub fn example_value(&self) -> &'static str {
1066 match self {
1067 LemmaType::Text => "\"hello world\"",
1068 LemmaType::Number => "3.14",
1069 LemmaType::Boolean => "true",
1070 LemmaType::Money => "99.99 EUR",
1071 LemmaType::Date => "2023-12-25T14:30:00Z",
1072 LemmaType::Duration => "90 minutes",
1073 LemmaType::Mass => "5.5 kilograms",
1074 LemmaType::Length => "10 meters",
1075 LemmaType::Percentage => "50%",
1076 LemmaType::Temperature => "25 celsius",
1077 LemmaType::Regex => "/pattern/",
1078 LemmaType::Volume => "1.2 liter",
1079 LemmaType::Power => "100 watts",
1080 LemmaType::Energy => "1000 joules",
1081 LemmaType::Force => "10 newtons",
1082 LemmaType::Pressure => "101325 pascals",
1083 LemmaType::Frequency => "880 hertz",
1084 LemmaType::Data => "800 megabytes",
1085 }
1086 }
1087}
1088
1089impl TypeAnnotation {
1090 pub fn example_value(&self) -> &'static str {
1092 match self {
1093 TypeAnnotation::LemmaType(lemma_type) => lemma_type.example_value(),
1094 }
1095 }
1096}
1097
1098impl fmt::Display for FactValue {
1099 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1100 match self {
1101 FactValue::Literal(lit) => write!(f, "{}", lit),
1102 FactValue::TypeAnnotation(type_ann) => write!(f, "[{}]", type_ann),
1103 FactValue::DocumentReference(doc_name) => write!(f, "doc {}", doc_name),
1104 }
1105 }
1106}
1107
1108impl fmt::Display for FactReference {
1109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1110 write!(f, "{}", self.reference.join("."))
1111 }
1112}
1113
1114impl fmt::Display for ForeignFact {
1115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1116 write!(f, "{}", self.reference.join("."))
1117 }
1118}
1119
1120impl fmt::Display for FactType {
1121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1122 match self {
1123 FactType::Local(name) => write!(f, "{}", name),
1124 FactType::Foreign(foreign_ref) => write!(f, "{}", foreign_ref),
1125 }
1126 }
1127}
1128
1129impl fmt::Display for RuleReference {
1130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1131 write!(f, "{}?", self.reference.join("."))
1132 }
1133}
1134
1135impl fmt::Display for ArithmeticOperation {
1136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1137 match self {
1138 ArithmeticOperation::Add => write!(f, "+"),
1139 ArithmeticOperation::Subtract => write!(f, "-"),
1140 ArithmeticOperation::Multiply => write!(f, "*"),
1141 ArithmeticOperation::Divide => write!(f, "/"),
1142 ArithmeticOperation::Modulo => write!(f, "%"),
1143 ArithmeticOperation::Power => write!(f, "^"),
1144 }
1145 }
1146}
1147
1148impl fmt::Display for ComparisonOperator {
1149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1150 match self {
1151 ComparisonOperator::GreaterThan => write!(f, ">"),
1152 ComparisonOperator::LessThan => write!(f, "<"),
1153 ComparisonOperator::GreaterThanOrEqual => write!(f, ">="),
1154 ComparisonOperator::LessThanOrEqual => write!(f, "<="),
1155 ComparisonOperator::Equal => write!(f, "=="),
1156 ComparisonOperator::NotEqual => write!(f, "!="),
1157 ComparisonOperator::Is => write!(f, "is"),
1158 ComparisonOperator::IsNot => write!(f, "is not"),
1159 }
1160 }
1161}
1162
1163impl fmt::Display for TimeValue {
1164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1165 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)
1166 }
1167}
1168
1169impl fmt::Display for TimezoneValue {
1170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1171 if self.offset_hours == 0 && self.offset_minutes == 0 {
1172 write!(f, "Z")
1173 } else {
1174 let sign = if self.offset_hours >= 0 { "+" } else { "-" };
1175 let hours = self.offset_hours.abs();
1176 write!(f, "{}{:02}:{:02}", sign, hours, self.offset_minutes)
1177 }
1178 }
1179}
1180
1181impl fmt::Display for DateTimeValue {
1182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1183 write!(
1184 f,
1185 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
1186 self.year, self.month, self.day, self.hour, self.minute, self.second
1187 )?;
1188 if let Some(tz) = &self.timezone {
1189 write!(f, "{}", tz)?;
1190 }
1191 Ok(())
1192 }
1193}
1194
1195#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1200pub struct RulePathSegment {
1201 pub fact: String,
1202 pub doc: String,
1203}
1204
1205#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1210pub struct RulePath {
1211 pub rule: String,
1212 pub segments: Vec<RulePathSegment>,
1213}
1214
1215impl RulePath {
1216 pub fn from_reference(
1217 reference: &[String],
1218 current_doc: &LemmaDoc,
1219 all_documents: &std::collections::HashMap<String, LemmaDoc>,
1220 ) -> Result<Self, crate::LemmaError> {
1221 let mut doc = current_doc;
1222 let mut segments = Vec::new();
1223
1224 for fact_name in &reference[..reference.len() - 1] {
1225 let fact = doc
1226 .facts
1227 .iter()
1228 .find(|f| matches!(&f.fact_type, FactType::Local(name) if name == fact_name))
1229 .ok_or_else(|| {
1230 crate::LemmaError::Engine(format!(
1231 "Fact {} not found in document {}",
1232 fact_name, doc.name
1233 ))
1234 })?;
1235
1236 let target_doc_name = match &fact.value {
1237 FactValue::DocumentReference(name) => name.clone(),
1238 _ => {
1239 return Err(crate::LemmaError::Engine(format!(
1240 "Fact {} is not a document reference",
1241 fact_name
1242 )))
1243 }
1244 };
1245
1246 segments.push(RulePathSegment {
1247 fact: fact_name.clone(),
1248 doc: target_doc_name.clone(),
1249 });
1250
1251 doc = all_documents.get(&target_doc_name).ok_or_else(|| {
1252 crate::LemmaError::Engine(format!("Document {} not found", target_doc_name))
1253 })?;
1254 }
1255
1256 let rule_name = reference.last().ok_or_else(|| {
1258 crate::LemmaError::Engine("Rule reference cannot be empty".to_string())
1259 })?;
1260
1261 Ok(RulePath {
1262 rule: rule_name.clone(),
1263 segments,
1264 })
1265 }
1266
1267 pub fn target_doc<'a>(&'a self, main_doc: &'a str) -> &'a str {
1268 self.segments
1269 .last()
1270 .map(|s| s.doc.as_str())
1271 .unwrap_or(main_doc)
1272 }
1273}
1274
1275impl fmt::Display for RulePath {
1276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1277 for seg in &self.segments {
1278 write!(f, "{}.", seg.fact)?;
1279 }
1280 write!(f, "{}", self.rule)
1281 }
1282}