1use crate::bif::Bif;
4use crate::closure::Closure;
5use crate::context::FeelContext;
6use crate::errors::*;
7use crate::names::Name;
8use crate::strings::ToFeelString;
9use crate::types::FeelType;
10use crate::FunctionBody;
11use dsntk_common::{Jsonify, Result};
12use dsntk_feel_number::FeelNumber;
13use dsntk_feel_temporal::{FeelDate, FeelDateTime, FeelDaysAndTimeDuration, FeelTime, FeelYearsAndMonthsDuration};
14use std::borrow::Borrow;
15use std::collections::BTreeMap;
16use std::fmt;
17use std::fmt::Display;
18use std::ops::Deref;
19use std::str::FromStr;
20
21#[macro_export]
38macro_rules! value_null {
39 ($module:expr, $function:literal, $format:literal, $($arguments:tt)*) => {
40 Value::Null(Some(format!("[{}::{}] {}", $module, $function, format!($format, $($arguments)*))))
41 };
42 ($format:literal, $($arguments:tt)*) => {
43 Value::Null(Some(format!($format, $($arguments)*)))
44 };
45 ($argument:expr) => {
46 Value::Null(Some(format!("{}", $argument)))
47 };
48 () => {
49 Value::Null(None)
50 };
51}
52
53#[macro_export]
54macro_rules! value_number {
55 ($n:expr) => {{
56 Value::Number($n.into())
57 }};
58 ($n:expr, $s:expr) => {
59 Value::Number(FeelNumber::new($n, $s))
60 };
61}
62
63#[macro_export]
64macro_rules! value_string {
65 ($s:literal) => {{
66 Value::String($s.to_string())
67 }};
68 ($s:expr) => {{
69 Value::String($s)
70 }};
71}
72
73pub const VALUE_TRUE: Value = Value::Boolean(true);
75
76pub const VALUE_FALSE: Value = Value::Boolean(false);
78
79const INVALID_COERCION: &str = "after coercion";
81
82#[derive(Debug, Clone, PartialEq)]
84pub enum Value {
85 Boolean(bool),
87
88 BuiltInFunction(Bif),
90
91 Context(FeelContext),
93
94 ContextEntry(Name, Box<Value>),
96
97 ContextEntryKey(Name),
99
100 ContextType(FeelType),
102
103 ContextTypeEntry(Name, FeelType),
105
106 ContextTypeEntryKey(Name),
108
109 Date(FeelDate),
111
112 DateTime(FeelDateTime),
114
115 DaysAndTimeDuration(FeelDaysAndTimeDuration),
117
118 ExpressionList(Values),
120
121 ExternalJavaFunction(
123 String,
125 String,
127 ),
128
129 ExternalPmmlFunction(
131 String,
133 String,
135 ),
136
137 FeelType(FeelType),
139
140 FormalParameter(Name, FeelType),
142
143 FormalParameters(Vec<(Name, FeelType)>),
145
146 FunctionBody(
148 FunctionBody,
150 bool,
152 ),
153
154 FunctionDefinition(
157 Vec<(Name, FeelType)>,
159 FunctionBody,
161 bool,
163 Closure,
165 FeelContext,
167 FeelType,
169 ),
170
171 IntervalEnd(Box<Value>, bool),
173
174 IntervalStart(Box<Value>, bool),
176
177 Irrelevant,
179
180 List(Values),
182
183 NamedParameter(Box<Value>, Box<Value>),
185
186 NamedParameters(BTreeMap<Name, (Value, usize)>),
188
189 NegatedCommaList(Values),
191
192 Null(Option<String>),
194
195 Number(FeelNumber),
197
198 ParameterName(Name),
200
201 ParameterTypes(Vec<Value>),
203
204 PositionalParameters(Values),
206
207 QualifiedNameSegment(Name),
209
210 Range(Box<Value>, bool, Box<Value>, bool),
212
213 String(String),
215
216 Time(FeelTime),
218
219 UnaryGreater(
221 Box<Value>,
223 ),
224
225 UnaryGreaterOrEqual(
227 Box<Value>,
229 ),
230
231 UnaryLess(
233 Box<Value>,
235 ),
236
237 UnaryLessOrEqual(
239 Box<Value>,
241 ),
242
243 UnaryEqual(
245 Box<Value>,
247 ),
248
249 UnaryNotEqual(
251 Box<Value>,
253 ),
254
255 Transparent(Box<Value>),
257
258 YearsAndMonthsDuration(FeelYearsAndMonthsDuration),
260}
261
262impl Display for Value {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 match self {
266 Value::Boolean(value) => write!(f, "{value}"),
267 Value::BuiltInFunction(_) => write!(f, "BuiltInFunction"),
268 Value::Context(context) => write!(f, "{context}"),
269 Value::ContextEntry(_, _) => write!(f, "ContextEntry"),
270 Value::ContextEntryKey(name) => write!(f, "{name}"),
271 Value::ContextType(_) => write!(f, "ContextType"),
272 Value::ContextTypeEntry(name, feel_type) => write!(f, "{name}: {feel_type}"),
273 Value::ContextTypeEntryKey(name) => write!(f, "{name}"),
274 Value::Date(date) => write!(f, "{date}"),
275 Value::DateTime(date_time) => write!(f, "{date_time}"),
276 Value::DaysAndTimeDuration(dt_duration) => write!(f, "{dt_duration}"),
277 Value::ExpressionList(items) => write!(f, "{}", values_to_string(items)),
278 Value::ExternalJavaFunction(class_name, method_signature) => write!(f, "ExternalJavaFunction({class_name}, {method_signature})"),
279 Value::ExternalPmmlFunction(iri, model_name) => write!(f, "ExternalPmmlFunction({iri}, {model_name})"),
280 Value::FeelType(feel_type) => write!(f, "type({feel_type})"),
281 Value::FormalParameter(_, _) => write!(f, "FormalParameter"),
282 Value::FormalParameters(_) => write!(f, "FormalParameters"),
283 Value::FunctionBody(_, external) => write!(f, "FunctionBody{}", if *external { " (external)" } else { "" }),
284 Value::FunctionDefinition(parameters, _body, external, closure, closure_ctx, return_type) => {
285 write!(f, "FunctionDefinition({parameters:?},_,{external},{closure},{closure_ctx},{return_type})")
286 }
287 Value::IntervalEnd(_, _) => write!(f, "IntervalEnd"),
288 Value::IntervalStart(_, _) => write!(f, "IntervalStart"),
289 Value::Irrelevant => write!(f, "Irrelevant"),
290 Value::List(items) => write!(f, "{}", values_to_string(items)),
291 Value::NamedParameter(_, _) => write!(f, "NamedParameter"),
292 Value::NamedParameters(_) => write!(f, "NamedParameters"),
293 Value::NegatedCommaList(_) => write!(f, "NegatedCommaList"),
294 Value::Number(value) => write!(f, "{value}"),
295 Value::Null(trace) => write!(f, "null{}", trace.as_ref().map_or("".to_string(), |s| format!("({s})"))),
296 Value::ParameterName(_) => write!(f, "ParameterName"),
297 Value::ParameterTypes(_) => write!(f, "ParameterTypes"),
298 Value::PositionalParameters(_) => write!(f, "PositionalParameters"),
299 Value::QualifiedNameSegment(_) => write!(f, "QualifiedNameSegment"),
300 Value::Range(v1, c1, v2, c2) => write!(
301 f,
302 "{}{}..{}{}",
303 if *c1 { '[' } else { '(' },
304 if v1.is_null() { "".to_string() } else { v1.to_string() },
305 if v2.is_null() { "".to_string() } else { v2.to_string() },
306 if *c2 { ']' } else { ')' }
307 ),
308 Value::String(s) => write!(f, "\"{s}\""),
309 Value::Time(time) => write!(f, "{time}"),
310 Value::UnaryGreater(value) => write!(f, "UnaryGreater({value})"),
311 Value::UnaryGreaterOrEqual(value) => write!(f, "UnaryGreaterOrEqual({value})"),
312 Value::UnaryLess(value) => write!(f, "UnaryLess({value})"),
313 Value::UnaryLessOrEqual(value) => write!(f, "UnaryLessOrEqual({value})"),
314 Value::UnaryEqual(value) => write!(f, "UnaryEqual({value})"),
315 Value::UnaryNotEqual(value) => write!(f, "UnaryNotEqual({value})"),
316 Value::Transparent(value) => write!(f, "Transparent({value})"),
317 Value::YearsAndMonthsDuration(ym_duration) => write!(f, "{ym_duration}"),
318 }
319 }
320}
321
322impl ToFeelString for Value {
323 fn to_feel_string(&self) -> String {
325 match self {
326 Value::Context(context) => context.to_feel_string(),
327 Value::List(items) => values_to_feel_string(items),
328 Value::String(value) => format!("\"{}\"", value.replace('"', "\\\"")),
329 other => other.to_string(),
330 }
331 }
332}
333
334impl Jsonify for Value {
335 fn jsonify(&self) -> String {
337 match self {
338 Value::Boolean(value) => format!("{value}"),
339 Value::Number(value) => value.jsonify(),
340 Value::String(s) => format!(r#""{s}""#),
341 Value::Date(date) => format!(r#""{}""#, date),
342 Value::Time(time) => format!(r#""{}""#, time),
343 Value::DateTime(date_time) => format!(r#""{}""#, date_time),
344 Value::DaysAndTimeDuration(dt_duration) => format!(r#""{}""#, dt_duration),
345 Value::YearsAndMonthsDuration(ym_duration) => format!(r#""{}""#, ym_duration),
346 Value::ExpressionList(items) => values_to_jsonify(items),
347 Value::Context(ctx) => ctx.jsonify(),
348 Value::ContextEntryKey(name) => name.to_string(),
349 Value::List(items) => values_to_jsonify(items),
350 range @ Value::Range(..) => format!(r#""{}""#, range),
351 Value::Null(message) => {
352 if let Some(details) = message {
353 format!(r#""null({details})""#)
354 } else {
355 "null".to_string()
356 }
357 }
358 _ => format!("jsonify trait not implemented for value: {self}"),
359 }
360 }
361}
362
363impl Value {
364 pub fn is_null(&self) -> bool {
366 matches!(self, Value::Null(_))
367 }
368
369 pub fn is_true(&self) -> bool {
371 matches!(self, Value::Boolean(true))
372 }
373
374 pub fn is_false(&self) -> bool {
376 matches!(self, Value::Boolean(false))
377 }
378
379 pub fn is_number(&self) -> bool {
381 matches!(self, Value::Number(_))
382 }
383
384 pub fn is_string(&self) -> bool {
386 matches!(self, Value::String(_))
387 }
388
389 pub fn is_list(&self) -> bool {
391 matches!(self, Value::List(_))
392 }
393
394 pub fn is_range(&self) -> bool {
396 matches!(self, Value::Range(_, _, _, _))
397 }
398
399 pub fn is_valid_range(&self) -> bool {
401 if let Value::Range(start, start_closed, end, end_closed) = self {
402 match start.borrow() {
403 Value::String(start) => match end.borrow() {
404 Value::String(end) => start <= end,
405 Value::Null(_) => !*end_closed,
406 _ => false,
407 },
408 Value::Number(start) => match end.borrow() {
409 Value::Number(end) => start <= end,
410 Value::Null(_) => !*end_closed,
411 _ => false,
412 },
413 Value::Date(start) => match end.borrow() {
414 Value::Date(end) => start <= end,
415 Value::Null(_) => !*end_closed,
416 _ => false,
417 },
418 Value::DateTime(start) => match end.borrow() {
419 Value::DateTime(end) => start <= end,
420 Value::Null(_) => !*end_closed,
421 _ => false,
422 },
423 Value::Time(start) => match end.borrow() {
424 Value::Time(end) => start <= end,
425 Value::Null(_) => !*end_closed,
426 _ => false,
427 },
428 Value::YearsAndMonthsDuration(start) => match end.borrow() {
429 Value::YearsAndMonthsDuration(end) => start <= end,
430 Value::Null(_) => !*end_closed,
431 _ => false,
432 },
433 Value::DaysAndTimeDuration(start) => match end.borrow() {
434 Value::DaysAndTimeDuration(end) => start <= end,
435 Value::Null(_) => !*end_closed,
436 _ => false,
437 },
438 Value::Null(_) => match end.borrow() {
439 Value::Null(_) => false,
440 _ => !*start_closed,
441 },
442 _ => false,
443 }
444 } else {
445 false
446 }
447 }
448
449 pub fn is_invalid_coercion(&self) -> bool {
451 if let Value::Null(Some(message)) = self {
452 message == INVALID_COERCION
453 } else {
454 false
455 }
456 }
457
458 pub fn type_of(&self) -> FeelType {
460 match self {
461 Value::Boolean(_) => FeelType::Boolean,
462 Value::BuiltInFunction(_) => FeelType::Any,
463 Value::Context(context) => {
464 let mut entries = BTreeMap::new();
465 for (name, value) in context.deref() {
466 entries.insert(name.clone(), value.type_of());
467 }
468 FeelType::Context(entries)
469 }
470 Value::ContextEntry(_, _) => FeelType::Any,
471 Value::ContextEntryKey(_) => FeelType::Any,
472 Value::ContextType(feel_type) => feel_type.clone(),
473 Value::ContextTypeEntry(_, feel_type) => feel_type.clone(),
474 Value::ContextTypeEntryKey(_) => FeelType::Any,
475 Value::Date(_) => FeelType::Date,
476 Value::DateTime(_) => FeelType::DateTime,
477 Value::DaysAndTimeDuration(_) => FeelType::DaysAndTimeDuration,
478 Value::ExpressionList(_) => FeelType::Any,
479 Value::ExternalJavaFunction(_, _) => FeelType::Any,
480 Value::ExternalPmmlFunction(_, _) => FeelType::Any,
481 Value::FeelType(feel_type) => feel_type.clone(),
482 Value::FormalParameter(_, feel_type) => feel_type.clone(),
483 Value::FormalParameters(_) => FeelType::Any,
484 Value::FunctionBody(_, _) => FeelType::Any,
485 Value::FunctionDefinition(parameters, _, _, _, _, result_type) => {
486 let parameter_types = parameters.iter().map(|(_, feel_type)| feel_type.clone()).collect();
487 FeelType::Function(parameter_types, Box::new(result_type.clone()))
488 }
489 Value::IntervalEnd(interval_end, _) => interval_end.type_of(),
490 Value::IntervalStart(interval_start, _) => interval_start.type_of(),
491 Value::Irrelevant => FeelType::Any,
492 Value::List(values) => {
493 if values.is_empty() {
494 FeelType::List(Box::new(FeelType::Null))
495 } else {
496 let item_type = values[0].type_of();
497 for item in values {
498 if !item.type_of().is_conformant(&item_type) {
499 return FeelType::List(Box::new(FeelType::Any));
500 }
501 }
502 FeelType::List(Box::new(item_type))
503 }
504 }
505 Value::NamedParameter(_, _) => FeelType::Any,
506 Value::NamedParameters(_) => FeelType::Any,
507 Value::NegatedCommaList(_) => FeelType::Any,
508 Value::Null(_) => FeelType::Null,
509 Value::Number(_) => FeelType::Number,
510 Value::ParameterName(_) => FeelType::Any,
511 Value::ParameterTypes(_) => FeelType::Any,
512 Value::PositionalParameters(_) => FeelType::Any,
513 Value::QualifiedNameSegment(_) => FeelType::Any,
514 Value::Range(range_start, _, range_end, _) => {
515 let range_start_type = range_start.type_of();
516 let range_end_type = range_end.type_of();
517 if range_start_type == range_end_type {
518 return FeelType::Range(Box::new(range_start_type));
519 }
520 FeelType::Range(Box::new(FeelType::Any))
521 }
522 Value::String(_) => FeelType::String,
523 Value::Time(_) => FeelType::Time,
524 Value::UnaryGreater(_) => FeelType::Boolean,
525 Value::UnaryGreaterOrEqual(_) => FeelType::Boolean,
526 Value::UnaryLess(_) => FeelType::Boolean,
527 Value::UnaryLessOrEqual(_) => FeelType::Boolean,
528 Value::UnaryEqual(_) => FeelType::Boolean,
529 Value::UnaryNotEqual(_) => FeelType::Boolean,
530 Value::Transparent(value) => value.type_of(),
531 Value::YearsAndMonthsDuration(_) => FeelType::YearsAndMonthsDuration,
532 }
533 }
534
535 pub fn is_conformant(&self, target_type: &FeelType) -> bool {
537 if matches!(target_type, FeelType::Any) {
538 return true;
539 }
540 match self {
541 Value::Null(_) => true,
542 Value::Boolean(_) => matches!(target_type, FeelType::Boolean),
543 Value::Number(_) => matches!(target_type, FeelType::Number),
544 Value::String(_) => matches!(target_type, FeelType::String),
545 Value::Date(_) => matches!(target_type, FeelType::Date),
546 Value::Time(_) => matches!(target_type, FeelType::Time),
547 Value::DateTime(_) => matches!(target_type, FeelType::DateTime),
548 Value::DaysAndTimeDuration(_) => matches!(target_type, FeelType::DaysAndTimeDuration),
549 Value::YearsAndMonthsDuration(_) => matches!(target_type, FeelType::YearsAndMonthsDuration),
550 Value::Range(r_start, _, r_end, _) => {
551 if let FeelType::Range(range_type) = target_type {
552 return r_start.is_conformant(range_type) && r_end.is_conformant(range_type);
553 }
554 false
555 }
556 Value::List(items) => {
557 if let FeelType::List(list_type) = target_type {
558 for item in items {
559 if !item.is_conformant(list_type) {
560 return false;
561 }
562 }
563 return true;
564 }
565 false
566 }
567 Value::Context(context) => {
568 if let FeelType::Context(type_context) = target_type {
569 for (name, entry_type) in type_context.iter() {
570 if let Some(entry_value) = context.get(name) {
571 if !entry_value.is_conformant(entry_type) {
572 return false;
573 }
574 } else {
575 return false;
576 }
577 }
578 return true;
579 }
580 false
581 }
582 Value::FunctionDefinition(parameters, _, _, _, _, result_type) => {
583 if let FeelType::Function(t_parameters, t_result) = target_type {
584 if parameters.len() != t_parameters.len() {
585 return false;
586 }
587 if !result_type.is_conformant(t_result) {
588 return false;
589 }
590 for (i, (_, parameter_type)) in parameters.iter().enumerate() {
591 if !parameter_type.is_conformant(&t_parameters[i]) {
592 return false;
593 }
594 }
595 return true;
596 }
597 false
598 }
599 _ => false,
600 }
601 }
602
603 pub fn coerced(&self, target_type: &FeelType) -> Value {
631 if let Value::FunctionDefinition(_, _, _, _, _, _) = self {
632 return self.clone();
633 }
634 if let Value::BuiltInFunction(bif) = self {
635 if bif.feel_type().is_conformant(target_type) {
636 return self.clone();
637 }
638 }
639 if self.is_conformant(target_type) {
640 return self.clone();
641 }
642 match self {
643 Value::List(items) => {
645 if items.len() == 1 {
646 let value = items[0].clone();
647 if value.is_conformant(target_type) {
648 return value;
649 }
650 }
651 }
652 value => {
654 if let FeelType::List(list_type) = target_type {
655 if value.is_conformant(list_type) {
656 return Value::List(vec![value.clone()]);
657 }
658 }
659 }
660 }
661 value_null!(INVALID_COERCION)
662 }
663
664 pub fn try_from_xsd_integer(text: &str) -> Result<Self> {
666 let value = text.parse::<FeelNumber>().map_err(|_| err_invalid_xsd_integer(text))?;
667 Ok(Value::Number(value))
668 }
669
670 pub fn try_from_xsd_decimal(text: &str) -> Result<Self> {
672 let value = text.parse::<FeelNumber>().map_err(|_| err_invalid_xsd_decimal(text))?;
673 Ok(Value::Number(value))
674 }
675
676 pub fn try_from_xsd_double(text: &str) -> Result<Self> {
678 let value = text.parse::<FeelNumber>().map_err(|_| err_invalid_xsd_double(text))?;
679 Ok(Value::Number(value))
680 }
681
682 pub fn try_from_xsd_boolean(text: &str) -> Result<Self> {
684 match text {
685 "true" | "1" => Ok(Value::Boolean(true)),
686 "false" | "0" => Ok(Value::Boolean(false)),
687 _ => Err(err_invalid_xsd_boolean(text)),
688 }
689 }
690
691 pub fn try_from_xsd_date(text: &str) -> Result<Self> {
694 if let Ok(feel_date) = FeelDate::from_str(text) {
695 return Ok(Value::Date(feel_date));
696 }
697 Err(err_invalid_xsd_date(text))
698 }
699
700 pub fn try_from_xsd_time(text: &str) -> Result<Self> {
703 if let Ok(feel_time) = FeelTime::from_str(text) {
704 return Ok(Value::Time(feel_time));
705 }
706 Err(err_invalid_xsd_time(text))
707 }
708
709 pub fn try_from_xsd_date_time(text: &str) -> Result<Self> {
712 Ok(Value::DateTime(FeelDateTime::try_from(text).map_err(|_| err_invalid_xsd_date_time(text))?))
713 }
714
715 pub fn try_from_xsd_duration(text: &str) -> Result<Self> {
718 if let Ok(ym_duration) = FeelYearsAndMonthsDuration::try_from(text) {
719 return Ok(Value::YearsAndMonthsDuration(ym_duration));
720 }
721 if let Ok(dt_duration) = FeelDaysAndTimeDuration::try_from(text) {
722 return Ok(Value::DaysAndTimeDuration(dt_duration));
723 }
724 Err(err_invalid_xsd_duration(text))
725 }
726}
727
728pub type Values = Vec<Value>;
730
731pub fn values_to_string(values: &Values) -> String {
733 format!("[{}]", values.iter().map(|value| value.to_string()).collect::<Vec<String>>().join(", "))
734}
735
736pub fn values_to_feel_string(values: &Values) -> String {
738 format!("[{}]", values.iter().map(|value| value.to_feel_string()).collect::<Vec<String>>().join(", "))
739}
740
741pub fn values_to_jsonify(values: &Values) -> String {
743 format!("[{}]", values.iter().map(|value| value.jsonify()).collect::<Vec<String>>().join(", "))
744}