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