dsntk_feel/
types.rs

1//! # FEEL types
2
3use 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  /// Type representing any valid FEEL type.
28  Any,
29
30  /// Type representing a `boolean` value.
31  Boolean,
32
33  /// Type representing a `context` value.
34  Context(
35    /// Types of context entries.
36    BTreeMap<Name, FeelType>,
37  ),
38
39  /// Type representing a `date` value.
40  Date,
41
42  /// Type representing a`date and time` value.
43  DateTime,
44
45  /// Type representing a `days and time duration` value.
46  DaysAndTimeDuration,
47
48  /// Type representing a `function` value.
49  Function(
50    /// List of types of the function's parameters.
51    Vec<FeelType>,
52    /// Type of the function's result.
53    Box<FeelType>,
54  ),
55
56  /// Type representing a `list` of values.
57  List(Box<FeelType>),
58
59  /// Type representing a `null` value.
60  Null,
61
62  /// Type representing a `number` value.
63  Number,
64
65  /// Type representing a `range` values.
66  Range(Box<FeelType>),
67
68  /// Type representing a `string` value.
69  String,
70
71  /// Type representing a `time` value.
72  Time,
73
74  /// Type representing a `years and months duration` value.
75  YearsAndMonthsDuration,
76}
77
78impl fmt::Display for FeelType {
79  /// Converts [FeelType] to into string.
80  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  /// Converts a string to built-in type.
118  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  /// Converts a FEEL name to built-in type.
137  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
146/// Returns `true` when the specified type name is a built-in type.
147pub 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  /// Returns a new value cloned from provided value, and retrieved with type checking.
176  pub fn get_value_checked(&self, value: &Value) -> Result<Value> {
177    if let Value::Null(_) = value {
178      // `null` value is conformant with all types
179      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  /// Returns `true` when this type is a simple FEEL type.
261  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  /// Returns `true` when this type is a Null type.
269  pub fn is_null(&self) -> bool {
270    matches!(self, Self::Null)
271  }
272
273  /// Creates a `list` type with specified items' type.
274  pub fn list(items_type: &FeelType) -> FeelType {
275    FeelType::List(Box::new(items_type.clone()))
276  }
277
278  /// Creates a `range` type with specified elements' type.
279  pub fn range(elements_type: &FeelType) -> FeelType {
280    FeelType::Range(Box::new(elements_type.clone()))
281  }
282
283  /// Creates a `context` type with specified entries.
284  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  /// Creates a `function` type with specified parameter types and result type.
289  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  /// Checks if this type is conformant with specified target type.
294  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(&parameters_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  /// Checks if this type is an instance of the other type.
355  pub fn instance_of(&self, other: &FeelType) -> bool {
356    // list
357    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    // context
363    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}