Skip to main content

lemma/api/
value.rs

1//! ValueKind / LiteralValue / RuleResultValue JSON shapes.
2
3use crate::literals::rational_to_serialized_str;
4use crate::planning::semantics::{
5    LiteralValue as DomainLiteralValue, SemanticDateTime, SemanticTime,
6    ValueKind as DomainValueKind,
7};
8use crate::result_value::{
9    CalendarResult as DomainCalendarResult, RangeResult as DomainRangeResult,
10    RuleResultValue as DomainRuleResultValue,
11};
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14
15/// Externally tagged value payload matching today's `ValueKind` Serialize shape.
16#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
17#[serde(rename_all = "lowercase")]
18pub enum ValueKind {
19    Number(String),
20    Measure {
21        value: String,
22    },
23    Ratio {
24        value: String,
25    },
26    Text(String),
27    Date(SemanticDateTime),
28    Time(SemanticTime),
29    Boolean(bool),
30    Range {
31        from: Box<ValueKind>,
32        to: Box<ValueKind>,
33    },
34}
35
36impl From<&DomainValueKind> for ValueKind {
37    fn from(value: &DomainValueKind) -> Self {
38        match value {
39            DomainValueKind::Number(rational) => Self::Number(
40                rational_to_serialized_str(rational)
41                    .expect("BUG: planned bound must serialize to decimal string"),
42            ),
43            DomainValueKind::Measure(rational) => Self::Measure {
44                value: rational_to_serialized_str(rational)
45                    .expect("BUG: planned bound must serialize to decimal string"),
46            },
47            DomainValueKind::Ratio(rational) => Self::Ratio {
48                value: rational_to_serialized_str(rational)
49                    .expect("BUG: planned bound must serialize to decimal string"),
50            },
51            DomainValueKind::Text(text) => Self::Text(text.clone()),
52            DomainValueKind::Date(date) => Self::Date(date.clone()),
53            DomainValueKind::Time(time) => Self::Time(time.clone()),
54            DomainValueKind::Boolean(flag) => Self::Boolean(*flag),
55            DomainValueKind::Range(from, to) => Self::Range {
56                from: Box::new(ValueKind::from(&from.value)),
57                to: Box::new(ValueKind::from(&to.value)),
58            },
59        }
60    }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
64pub struct LiteralValue {
65    pub value: ValueKind,
66}
67
68impl From<&DomainLiteralValue> for LiteralValue {
69    fn from(literal: &DomainLiteralValue) -> Self {
70        Self {
71            value: ValueKind::from(&literal.value),
72        }
73    }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77pub struct CalendarResult {
78    pub value: String,
79    pub unit: String,
80}
81
82impl From<&DomainCalendarResult> for CalendarResult {
83    fn from(calendar: &DomainCalendarResult) -> Self {
84        Self {
85            value: calendar.value.clone(),
86            unit: calendar.unit.clone(),
87        }
88    }
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
92pub struct RangeResult {
93    pub from: RuleResultValue,
94    pub to: RuleResultValue,
95}
96
97impl From<&DomainRangeResult> for RangeResult {
98    fn from(range: &DomainRangeResult) -> Self {
99        Self {
100            from: RuleResultValue::from(&range.from),
101            to: RuleResultValue::from(&range.to),
102        }
103    }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
107pub struct RuleResultValue {
108    #[serde(default, skip_serializing_if = "Option::is_none")]
109    pub display: Option<String>,
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub measure: Option<BTreeMap<String, String>>,
112    #[serde(default, skip_serializing_if = "Option::is_none")]
113    pub ratio: Option<BTreeMap<String, String>>,
114    #[serde(default, skip_serializing_if = "Option::is_none")]
115    pub number: Option<String>,
116    #[serde(default, skip_serializing_if = "Option::is_none")]
117    pub boolean: Option<bool>,
118    #[serde(default, skip_serializing_if = "Option::is_none")]
119    pub text: Option<String>,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub date: Option<SemanticDateTime>,
122    #[serde(default, skip_serializing_if = "Option::is_none")]
123    pub time: Option<SemanticTime>,
124    #[serde(default, skip_serializing_if = "Option::is_none")]
125    pub calendar: Option<CalendarResult>,
126    #[serde(default, skip_serializing_if = "Option::is_none")]
127    pub range: Option<Box<RangeResult>>,
128}
129
130impl From<&DomainRuleResultValue> for RuleResultValue {
131    fn from(value: &DomainRuleResultValue) -> Self {
132        Self {
133            display: value.display.clone(),
134            measure: value.measure.clone(),
135            ratio: value.ratio.clone(),
136            number: value.number.clone(),
137            boolean: value.boolean,
138            text: value.text.clone(),
139            date: value.date.clone(),
140            time: value.time.clone(),
141            calendar: value.calendar.as_ref().map(CalendarResult::from),
142            range: value
143                .range
144                .as_ref()
145                .map(|range| Box::new(RangeResult::from(range.as_ref()))),
146        }
147    }
148}