1use crate::context::FeelContext;
4use crate::errors::*;
5use crate::names::Name;
6use crate::value_null;
7use crate::values::Value;
8use dsntk_common::{DsntkError, Result};
9use std::collections::BTreeMap;
10use std::fmt;
11use std::str::FromStr;
12
13pub const FEEL_TYPE_NAME_ANY: &str = "Any";
14pub const FEEL_TYPE_NAME_BOOLEAN: &str = "boolean";
15pub const FEEL_TYPE_NAME_DATE: &str = "date";
16pub const FEEL_TYPE_NAME_DATE_AND_TIME: &str = "date and time";
17pub const FEEL_TYPE_NAME_DAYS_AND_TIME_DURATION: &str = "days and time duration";
18pub const FEEL_TYPE_NAME_NULL: &str = "Null";
19pub const FEEL_TYPE_NAME_NUMBER: &str = "number";
20pub const FEEL_TYPE_NAME_STRING: &str = "string";
21pub const FEEL_TYPE_NAME_TIME: &str = "time";
22pub const FEEL_TYPE_NAME_YEARS_AND_MONTHS_DURATION: &str = "years and months duration";
23
24#[derive(Debug, Clone, PartialEq)]
25#[must_use]
26pub enum FeelType {
27 Any,
29
30 Boolean,
32
33 Context(
35 BTreeMap<Name, FeelType>,
37 ),
38
39 Date,
41
42 DateTime,
44
45 DaysAndTimeDuration,
47
48 Function(
50 Vec<FeelType>,
52 Box<FeelType>,
54 ),
55
56 List(Box<FeelType>),
58
59 Null,
61
62 Number,
64
65 Range(Box<FeelType>),
67
68 String,
70
71 Time,
73
74 YearsAndMonthsDuration,
76}
77
78impl fmt::Display for FeelType {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 FeelType::Any => write!(f, "{FEEL_TYPE_NAME_ANY}"),
83 FeelType::Boolean => write!(f, "{FEEL_TYPE_NAME_BOOLEAN}"),
84 FeelType::Context(entries) => {
85 let entries_str = entries
86 .iter()
87 .map(|(entry_name, entry_type)| format!("{entry_name}: {entry_type}"))
88 .collect::<Vec<String>>()
89 .join(", ");
90 write!(f, "context<{entries_str}>",)
91 }
92 FeelType::Date => write!(f, "{FEEL_TYPE_NAME_DATE}"),
93 FeelType::DateTime => write!(f, "{FEEL_TYPE_NAME_DATE_AND_TIME}"),
94 FeelType::DaysAndTimeDuration => write!(f, "{FEEL_TYPE_NAME_DAYS_AND_TIME_DURATION}"),
95 FeelType::Function(parameter_types, result_type) => {
96 let parameter_types_str = parameter_types.iter().map(|parameter_type| format!("{parameter_type}")).collect::<Vec<String>>().join(", ");
97 let result_type_str = result_type.to_string();
98 write!(f, "function<{parameter_types_str}>->{result_type_str}")
99 }
100 FeelType::List(item_type) => {
101 write!(f, "list<{item_type}>")
102 }
103 FeelType::Null => write!(f, "{FEEL_TYPE_NAME_NULL}"),
104 FeelType::Number => write!(f, "{FEEL_TYPE_NAME_NUMBER}"),
105 FeelType::Range(range_type) => {
106 write!(f, "range<{range_type}>")
107 }
108 FeelType::String => write!(f, "{FEEL_TYPE_NAME_STRING}"),
109 FeelType::Time => write!(f, "{FEEL_TYPE_NAME_TIME}"),
110 FeelType::YearsAndMonthsDuration => write!(f, "{FEEL_TYPE_NAME_YEARS_AND_MONTHS_DURATION}"),
111 }
112 }
113}
114
115impl FromStr for FeelType {
116 type Err = DsntkError;
117 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 match s {
120 FEEL_TYPE_NAME_ANY => Ok(Self::Any),
121 FEEL_TYPE_NAME_BOOLEAN => Ok(Self::Boolean),
122 FEEL_TYPE_NAME_DATE => Ok(Self::Date),
123 FEEL_TYPE_NAME_DATE_AND_TIME => Ok(Self::DateTime),
124 FEEL_TYPE_NAME_DAYS_AND_TIME_DURATION => Ok(Self::DaysAndTimeDuration),
125 FEEL_TYPE_NAME_NULL => Ok(Self::Null),
126 FEEL_TYPE_NAME_NUMBER => Ok(Self::Number),
127 FEEL_TYPE_NAME_STRING => Ok(Self::String),
128 FEEL_TYPE_NAME_TIME => Ok(Self::Time),
129 FEEL_TYPE_NAME_YEARS_AND_MONTHS_DURATION => Ok(Self::YearsAndMonthsDuration),
130 _ => Err(err_invalid_feel_type_name(s)),
131 }
132 }
133}
134
135impl From<&Name> for FeelType {
136 fn from(name: &Name) -> Self {
138 if let Ok(feel_type) = Self::from_str(name.to_string().as_str()) {
139 feel_type
140 } else {
141 FeelType::Any
142 }
143 }
144}
145
146pub fn is_built_in_type_name(name: &str) -> bool {
148 matches!(
149 name,
150 FEEL_TYPE_NAME_ANY
151 | FEEL_TYPE_NAME_BOOLEAN
152 | FEEL_TYPE_NAME_DATE
153 | FEEL_TYPE_NAME_DATE_AND_TIME
154 | FEEL_TYPE_NAME_DAYS_AND_TIME_DURATION
155 | FEEL_TYPE_NAME_NULL
156 | FEEL_TYPE_NAME_NUMBER
157 | FEEL_TYPE_NAME_STRING
158 | FEEL_TYPE_NAME_TIME
159 | FEEL_TYPE_NAME_YEARS_AND_MONTHS_DURATION
160 )
161}
162
163impl FeelType {
164 pub fn get_conformant_value(&self, actual_value: &Value) -> Value {
165 if let FeelType::Null = self {
166 return value_null!();
167 }
168 if actual_value.is_conformant(self) {
169 self.get_value_checked(actual_value).unwrap()
170 } else {
171 value_null!("type '{}' is not conformant with value '{}'", self.to_string(), actual_value)
172 }
173 }
174
175 pub fn get_value_checked(&self, value: &Value) -> Result<Value> {
177 if let Value::Null(_) = value {
178 return Ok(value_null!());
180 }
181 match self {
182 FeelType::Any => {
183 return Ok(value.clone());
184 }
185 FeelType::Boolean => {
186 if let Value::Boolean(_) = value {
187 return Ok(value.clone());
188 }
189 }
190 FeelType::Context(entries) => {
191 if let Value::Context(context) = value {
192 let mut result = FeelContext::default();
193 for (name, entry_type) in entries {
194 if let Some(entry_value) = context.get_entry(name) {
195 result.set_entry(name, entry_type.get_value_checked(entry_value)?);
196 }
197 }
198 return Ok(Value::Context(result));
199 }
200 }
201 FeelType::Date => {
202 if let Value::Date(_) = value {
203 return Ok(value.clone());
204 }
205 }
206 FeelType::DateTime => {
207 if let Value::DateTime(_) = value {
208 return Ok(value.clone());
209 }
210 }
211 FeelType::DaysAndTimeDuration => {
212 if let Value::DaysAndTimeDuration(_) = value {
213 return Ok(value.clone());
214 }
215 }
216 FeelType::Function(_, _) => {
217 if let Value::FunctionDefinition { .. } = value {
218 return Ok(value.clone());
219 }
220 }
221 FeelType::List(items_type) => {
222 if let Value::List(items) = value {
223 let mut result = vec![];
224 for item in items {
225 result.push(items_type.get_value_checked(item)?);
226 }
227 return Ok(Value::List(result));
228 }
229 }
230 FeelType::Number => {
231 if let Value::Number(_) = value {
232 return Ok(value.clone());
233 }
234 }
235 FeelType::Range(_) => {
236 if let Value::Range(_, _, _, _) = value {
237 return Ok(value.clone());
238 }
239 }
240 FeelType::String => {
241 if let Value::String(_) = value {
242 return Ok(value.clone());
243 }
244 }
245 FeelType::Time => {
246 if let Value::Time(_) = value {
247 return Ok(value.clone());
248 }
249 }
250 FeelType::YearsAndMonthsDuration => {
251 if let Value::YearsAndMonthsDuration(_) = value {
252 return Ok(value.clone());
253 }
254 }
255 _ => {}
256 }
257 Err(err_invalid_value_for_retrieving_using_feel_type(&self.to_string(), &value.to_string()))
258 }
259
260 pub fn is_simple_built_in_type(&self) -> bool {
262 matches!(
263 self,
264 Self::Any | Self::Boolean | Self::Date | Self::DateTime | Self::DaysAndTimeDuration | Self::Null | Self::Number | Self::String | Self::Time | Self::YearsAndMonthsDuration
265 )
266 }
267
268 pub fn is_null(&self) -> bool {
270 matches!(self, Self::Null)
271 }
272
273 pub fn list(items_type: &FeelType) -> FeelType {
275 FeelType::List(Box::new(items_type.clone()))
276 }
277
278 pub fn range(elements_type: &FeelType) -> FeelType {
280 FeelType::Range(Box::new(elements_type.clone()))
281 }
282
283 pub fn context(entries_types: &[(&Name, &FeelType)]) -> FeelType {
285 FeelType::Context(entries_types.iter().map(|(name, typ)| ((*name).clone(), (*typ).clone())).collect())
286 }
287
288 pub fn function(parameter_types: &[FeelType], result_type: &FeelType) -> FeelType {
290 FeelType::Function(parameter_types.iter().map(|typ| (*typ).clone()).collect(), Box::new((*result_type).clone()))
291 }
292
293 pub fn is_conformant(&self, target_type: &FeelType) -> bool {
295 if matches!(self, FeelType::Null) {
296 return true;
297 }
298 match target_type {
299 FeelType::Any => return true,
300 FeelType::Null => return matches!(self, FeelType::Null),
301 FeelType::Boolean => return matches!(self, FeelType::Boolean),
302 FeelType::Number => return matches!(self, FeelType::Number),
303 FeelType::String => return matches!(self, FeelType::String),
304 FeelType::Date => return matches!(self, FeelType::Date),
305 FeelType::Time => return matches!(self, FeelType::Time),
306 FeelType::DateTime => return matches!(self, FeelType::DateTime),
307 FeelType::DaysAndTimeDuration => return matches!(self, FeelType::DaysAndTimeDuration),
308 FeelType::YearsAndMonthsDuration => return matches!(self, FeelType::YearsAndMonthsDuration),
309 FeelType::Range(type_other) => {
310 if let FeelType::Range(type_self) = self {
311 return type_self.is_conformant(type_other);
312 }
313 }
314 FeelType::List(type_other) => {
315 if let FeelType::List(type_self) = self {
316 return type_self.is_conformant(type_other);
317 }
318 }
319 FeelType::Context(target_entries) => {
320 if let FeelType::Context(self_entries) = self {
321 for (name, type_target) in target_entries {
322 if let Some(type_self) = self_entries.get(name) {
323 if !type_self.is_conformant(type_target) {
324 return false;
325 }
326 } else {
327 return false;
328 }
329 }
330 return true;
331 }
332 return false;
333 }
334 FeelType::Function(parameters_other, result_other) => {
335 if let FeelType::Function(parameters_self, result_self) = self {
336 if parameters_self.len() == parameters_other.len() {
337 for (i, parameter_other) in parameters_other.iter().enumerate() {
338 if !parameter_other.is_conformant(¶meters_self[i]) {
339 return false;
340 }
341 if !result_self.is_conformant(result_other) {
342 return false;
343 }
344 }
345 return true;
346 }
347 }
348 return false;
349 }
350 }
351 false
352 }
353
354 pub fn instance_of(&self, other: &FeelType) -> bool {
356 if let FeelType::List(self_inner_type) = self {
358 if let FeelType::List(other_inner_type) = other {
359 return self_inner_type == other_inner_type || **other_inner_type == FeelType::Any;
360 }
361 }
362 if let FeelType::Context(self_inner_type) = self {
364 if let FeelType::Context(other_inner_type) = other {
365 if self_inner_type == other_inner_type {
366 return true;
367 }
368 for (name, other_inner_feel_type) in other_inner_type {
369 if let Some(self_inner_feel_type) = self_inner_type.get(name) {
370 if !self_inner_feel_type.is_null() && self_inner_feel_type != other_inner_feel_type {
371 return false;
372 }
373 } else {
374 return false;
375 }
376 }
377 return true;
378 }
379 }
380 false
381 }
382}