helios_fhirpath_support/
lib.rs

1//! # FHIRPath Support Types
2//!
3//! This crate provides the foundational types and traits that serve as a bridge between
4//! the FHIRPath evaluator and the broader FHIR ecosystem. It defines the common data
5//! structures and conversion interfaces that enable seamless integration across all
6//! components of the FHIRPath implementation.
7//!
8//! ## Overview
9//!
10//! The fhirpath_support crate acts as the universal communication layer that allows:
11//! - FHIRPath evaluator to work with unified result types
12//! - FHIR data structures to convert into FHIRPath-compatible formats
13//! - Code generation macros to produce FHIRPath-aware implementations
14//! - Type conversion system to handle data transformation
15//!
16//! ## Core Types
17//!
18//! - [`EvaluationResult`] - Universal result type for FHIRPath expression evaluation
19//! - [`EvaluationError`] - Comprehensive error handling for evaluation failures
20//! - [`IntoEvaluationResult`] - Trait for converting types to evaluation results
21//!
22//! ## Usage Example
23//!
24//! ```rust
25//! use helios_fhirpath_support::{EvaluationResult, IntoEvaluationResult};
26//!
27//! // Convert a string to an evaluation result
28//! let text = "Hello, FHIR!".to_string();
29//! let result = text.to_evaluation_result();
30//! assert_eq!(result, EvaluationResult::String("Hello, FHIR!".to_string(), None));
31//!
32//! // Work with collections
33//! let numbers = vec![1, 2, 3];
34//! let collection = numbers.to_evaluation_result();
35//! assert_eq!(collection.count(), 3);
36//! ```
37
38use rust_decimal::Decimal;
39use rust_decimal::prelude::*;
40use std::cmp::Ordering;
41use std::collections::HashMap;
42use std::hash::{Hash, Hasher};
43
44mod type_info;
45pub use type_info::{TypeInfo, TypeInfoResult};
46
47/// Trait for FHIR choice element types.
48///
49/// This trait is implemented by generated enum types that represent FHIR choice elements
50/// (fields with [x] in the FHIR specification). It provides metadata about the choice
51/// element that enables proper polymorphic access in FHIRPath expressions.
52///
53/// # Example
54///
55/// For a FHIR field like `Observation.value[x]`, the generated enum would implement:
56/// ```rust,ignore
57/// impl ChoiceElement for ObservationValue {
58///     fn base_name() -> &'static str {
59///         "value"
60///     }
61///     
62///     fn possible_field_names() -> Vec<&'static str> {
63///         vec!["valueQuantity", "valueCodeableConcept", "valueString", ...]
64///     }
65/// }
66/// ```
67pub trait ChoiceElement {
68    /// Returns the base name of the choice element without the [x] suffix.
69    ///
70    /// For example, for `value[x]`, this returns "value".
71    fn base_name() -> &'static str;
72
73    /// Returns all possible field names that this choice element can manifest as.
74    ///
75    /// For example, for `value[x]`, this might return:
76    /// ["valueQuantity", "valueCodeableConcept", "valueString", ...]
77    fn possible_field_names() -> Vec<&'static str>;
78}
79
80/// Trait for FHIR resource metadata.
81///
82/// This trait is implemented by generated FHIR resource structs to provide
83/// metadata about the resource's structure, particularly which fields are
84/// choice elements. This enables accurate polymorphic field access in FHIRPath.
85///
86/// # Example
87///
88/// ```rust,ignore
89/// impl FhirResourceMetadata for Observation {
90///     fn choice_elements() -> &'static [&'static str] {
91///         &["value", "effective", "component.value"]
92///     }
93/// }
94/// ```
95pub trait FhirResourceMetadata {
96    /// Returns the names of all choice element fields in this resource.
97    ///
98    /// The returned slice contains the base names (without [x]) of fields
99    /// that are choice elements in the FHIR specification.
100    fn choice_elements() -> &'static [&'static str];
101}
102
103/// Universal conversion trait for transforming values into FHIRPath evaluation results.
104///
105/// This trait provides the bridge between FHIR data types and the FHIRPath evaluation
106/// system. It allows any type to be converted into an `EvaluationResult` that can be
107/// processed by FHIRPath expressions.
108///
109/// # Implementation Guidelines
110///
111/// When implementing this trait:
112/// - Return `EvaluationResult::Empty` for `None` or missing values
113/// - Use appropriate variant types (Boolean, String, Integer, etc.)
114/// - For complex types, use `EvaluationResult::Object` with field mappings
115/// - For arrays/collections, use `EvaluationResult::Collection`
116///
117/// # Examples
118///
119/// ```rust
120/// use helios_fhirpath_support::{EvaluationResult, IntoEvaluationResult};
121///
122/// struct CustomType {
123///     value: String,
124///     active: bool,
125/// }
126///
127/// impl IntoEvaluationResult for CustomType {
128///     fn to_evaluation_result(&self) -> EvaluationResult {
129///         let mut map = std::collections::HashMap::new();
130///         map.insert("value".to_string(), self.value.to_evaluation_result());
131///         map.insert("active".to_string(), self.active.to_evaluation_result());
132///         EvaluationResult::Object { map, type_info: None }
133///     }
134/// }
135/// ```
136pub trait IntoEvaluationResult {
137    /// Converts this value into a FHIRPath evaluation result.
138    ///
139    /// This method should transform the implementing type into the most appropriate
140    /// `EvaluationResult` variant that represents the value's semantics in FHIRPath.
141    fn to_evaluation_result(&self) -> EvaluationResult;
142}
143
144/// Universal result type for FHIRPath expression evaluation.
145///
146/// This enum represents any value that can result from evaluating a FHIRPath expression
147/// against FHIR data. It provides a unified type system that bridges FHIR's data model
148/// with FHIRPath's evaluation semantics.
149///
150/// # Variants
151///
152/// - **`Empty`**: Represents no value or null (equivalent to FHIRPath's empty collection)
153/// - **`Boolean`**: True/false values from boolean expressions
154/// - **`String`**: Text values from FHIR strings, codes, URIs, etc.
155/// - **`Decimal`**: High-precision decimal numbers for accurate numeric computation
156/// - **`Integer`**: Whole numbers for counting and indexing operations
157/// - **`Integer64`**: Explicit 64-bit integers for special cases
158/// - **`Date`**: Date values in ISO format (YYYY-MM-DD)
159/// - **`DateTime`**: DateTime values in ISO format with optional timezone
160/// - **`Time`**: Time values in ISO format (HH:MM:SS)
161/// - **`Quantity`**: Value with unit (e.g., "5.4 mg", "10 years")
162/// - **`Collection`**: Ordered collections of evaluation results
163/// - **`Object`**: Key-value structures representing complex FHIR types
164///
165/// # Type Safety
166///
167/// The enum is designed to prevent type errors at runtime by encoding FHIRPath's
168/// type system at the Rust type level. Operations that require specific types
169/// can pattern match on the appropriate variants.
170///
171/// # Examples
172///
173/// ```rust
174/// use helios_fhirpath_support::EvaluationResult;
175/// use rust_decimal::Decimal;
176///
177/// // Creating different result types
178/// let empty = EvaluationResult::Empty;
179/// let text = EvaluationResult::String("Patient".to_string(), None);
180/// let number = EvaluationResult::Integer(42, None);
181/// let number64 = EvaluationResult::Integer64(9223372036854775807, None); // max i64
182/// let decimal = EvaluationResult::Decimal(Decimal::new(1234, 2), None); // 12.34
183///
184/// // Working with collections
185/// let items = vec![text, number];
186/// let collection = EvaluationResult::Collection {
187///     items,
188///     has_undefined_order: false,
189///     type_info: None
190/// };
191///
192/// assert_eq!(collection.count(), 2);
193/// assert!(collection.is_collection());
194/// ```
195#[derive(Debug, Clone)]
196pub enum EvaluationResult {
197    /// No value or empty collection.
198    ///
199    /// Represents the absence of a value, equivalent to FHIRPath's empty collection `{}`.
200    /// This is the result when accessing non-existent properties or when filters
201    /// match no elements.
202    Empty,
203    /// Boolean true/false value.
204    ///
205    /// Results from boolean expressions, existence checks, and logical operations.
206    /// Also used for FHIR boolean fields.
207    Boolean(bool, Option<TypeInfoResult>),
208    /// Text string value.
209    ///
210    /// Used for FHIR string, code, uri, canonical, id, and other text-based types.
211    /// Also results from string manipulation functions and conversions.
212    String(String, Option<TypeInfoResult>),
213    /// High-precision decimal number.
214    ///
215    /// Uses `rust_decimal::Decimal` for precise arithmetic without floating-point
216    /// errors. Required for FHIR's decimal type and mathematical operations.
217    Decimal(Decimal, Option<TypeInfoResult>),
218    /// Whole number value.
219    ///
220    /// Used for FHIR integer, positiveInt, unsignedInt types and counting operations.
221    /// Also results from indexing and length functions.
222    Integer(i64, Option<TypeInfoResult>),
223    /// 64-bit integer value.
224    ///
225    /// Explicit 64-bit integer type for cases where the distinction from regular
226    /// integers is important.
227    Integer64(i64, Option<TypeInfoResult>),
228    /// Date value in ISO format.
229    ///
230    /// Stores date as string in YYYY-MM-DD format. Handles FHIR date fields
231    /// and results from date extraction functions.
232    Date(String, Option<TypeInfoResult>),
233    /// DateTime value in ISO format.
234    ///
235    /// Stores datetime as string in ISO 8601 format with optional timezone.
236    /// Handles FHIR dateTime and instant fields.
237    DateTime(String, Option<TypeInfoResult>),
238    /// Time value in ISO format.
239    ///
240    /// Stores time as string in HH:MM:SS format. Handles FHIR time fields
241    /// and results from time extraction functions.
242    Time(String, Option<TypeInfoResult>),
243    /// Quantity with value and unit.
244    ///
245    /// Represents measurements with units (e.g., "5.4 mg", "10 years").
246    /// First element is the numeric value, second is the unit string.
247    /// Used for FHIR Quantity, Age, Duration, Distance, Count, and Money types.
248    Quantity(Decimal, String, Option<TypeInfoResult>),
249    /// Ordered collection of evaluation results.
250    ///
251    /// Represents arrays, lists, and multi-valued FHIR elements. Collections
252    /// maintain order for FHIRPath operations like indexing and iteration.
253    ///
254    /// # Fields
255    ///
256    /// - `items`: The ordered list of contained evaluation results
257    /// - `has_undefined_order`: Flag indicating if the original source order
258    ///   was undefined (affects certain FHIRPath operations)
259    Collection {
260        /// The ordered items in this collection
261        items: Vec<EvaluationResult>,
262        /// Whether the original source order was undefined
263        has_undefined_order: bool,
264        /// Optional type information
265        type_info: Option<TypeInfoResult>,
266    },
267    /// Key-value object representing complex FHIR types.
268    ///
269    /// Used for FHIR resources, data types, and backbone elements. Keys are
270    /// field names and values are the corresponding evaluation results.
271    /// Enables property access via FHIRPath dot notation.
272    ///
273    /// The optional type_namespace and type_name fields preserve type information
274    /// for the FHIRPath type() function.
275    Object {
276        /// The object's properties
277        map: HashMap<String, EvaluationResult>,
278        /// Optional type information
279        type_info: Option<TypeInfoResult>,
280    },
281}
282
283/// Comprehensive error type for FHIRPath evaluation failures.
284///
285/// This enum covers all categories of errors that can occur during FHIRPath expression
286/// evaluation, from type mismatches to semantic violations. Each variant provides
287/// specific context about the failure to aid in debugging and error reporting.
288///
289/// # Error Categories
290///
291/// - **Type Errors**: Mismatched types in operations or function calls
292/// - **Argument Errors**: Invalid arguments passed to functions
293/// - **Runtime Errors**: Errors during expression evaluation (division by zero, etc.)
294/// - **Semantic Errors**: Violations of FHIRPath semantic rules
295/// - **System Errors**: Internal errors and edge cases
296///
297/// # Error Handling
298///
299/// All variants implement `std::error::Error` and `Display` for standard Rust
300/// error handling patterns. The error messages are designed to be user-friendly
301/// and provide actionable information for debugging.
302///
303/// # Examples
304///
305/// ```rust
306/// use helios_fhirpath_support::EvaluationError;
307///
308/// // Type error example
309/// let error = EvaluationError::TypeError(
310///     "Cannot add String and Integer".to_string()
311/// );
312///
313/// // Display the error
314/// println!("{}", error); // "Type Error: Cannot add String and Integer"
315/// ```
316#[derive(Debug, Clone, PartialEq, Eq)]
317pub enum EvaluationError {
318    /// Type mismatch or incompatible type operation.
319    ///
320    /// Occurs when operations are attempted on incompatible types or when
321    /// functions receive arguments of unexpected types.
322    ///
323    /// Example: "Expected Boolean, found Integer"
324    TypeError(String),
325    /// Invalid argument provided to a function.
326    ///
327    /// Occurs when function arguments don't meet the required constraints
328    /// or format expectations.
329    ///
330    /// Example: "Invalid argument for function 'where'"
331    InvalidArgument(String),
332    /// Reference to an undefined variable.
333    ///
334    /// Occurs when expressions reference variables that haven't been defined
335    /// in the current evaluation context.
336    ///
337    /// Example: "Variable '%undefinedVar' not found"
338    UndefinedVariable(String),
339    /// Invalid operation for the given operand types.
340    ///
341    /// Occurs when operators are used with incompatible operand types or
342    /// when operations are not supported for the given types.
343    ///
344    /// Example: "Cannot add String and Integer"
345    InvalidOperation(String),
346    /// Incorrect number of arguments provided to a function.
347    ///
348    /// Occurs when functions are called with too many or too few arguments
349    /// compared to their specification.
350    ///
351    /// Example: "Function 'substring' expects 1 or 2 arguments, got 3"
352    InvalidArity(String),
353    /// Invalid array or collection index.
354    ///
355    /// Occurs when collection indexing operations use invalid indices
356    /// (negative numbers, non-integers, out of bounds).
357    ///
358    /// Example: "Index must be a non-negative integer"
359    InvalidIndex(String),
360    /// Attempted division by zero.
361    ///
362    /// Occurs during mathematical operations when the divisor is zero.
363    /// This is a specific case of arithmetic error with clear semantics.
364    DivisionByZero,
365    /// Arithmetic operation resulted in overflow.
366    ///
367    /// Occurs when mathematical operations produce results that exceed
368    /// the representable range of the target numeric type.
369    ArithmeticOverflow,
370    /// Invalid regular expression pattern.
371    ///
372    /// Occurs when regex-based functions receive malformed regex patterns
373    /// that cannot be compiled.
374    ///
375    /// Example: "Invalid regex pattern: unclosed parenthesis"
376    InvalidRegex(String),
377    /// Invalid type specifier in type operations.
378    ///
379    /// Occurs when type checking operations (is, as, ofType) receive
380    /// invalid or unrecognized type specifiers.
381    ///
382    /// Example: "Unknown type 'InvalidType'"
383    InvalidTypeSpecifier(String),
384    /// Collection cardinality error for singleton operations.
385    ///
386    /// Occurs when operations expecting a single value receive collections
387    /// with zero or multiple items.
388    ///
389    /// Example: "Expected singleton, found collection with 3 items"
390    SingletonEvaluationError(String),
391    /// Semantic rule violation.
392    ///
393    /// Occurs when expressions violate FHIRPath semantic rules, such as
394    /// accessing non-existent properties in strict mode or violating
395    /// contextual constraints.
396    ///
397    /// Example: "Property 'invalidField' does not exist on type 'Patient'"
398    SemanticError(String),
399    /// Unsupported function called.
400    ///
401    /// Occurs when a FHIRPath function is recognized but not yet implemented
402    /// in this evaluation engine.
403    ///
404    /// Example: "Function 'conformsTo' is not implemented"
405    UnsupportedFunction(String),
406    /// Generic error for cases not covered by specific variants.
407    ///
408    /// Used for internal errors, edge cases, or temporary error conditions
409    /// that don't fit into the specific error categories.
410    ///
411    /// Example: "Internal evaluation error"
412    Other(String),
413}
414
415// === Standard Error Trait Implementations ===
416
417/// Implements the standard `Error` trait for `EvaluationError`.
418///
419/// This allows `EvaluationError` to be used with Rust's standard error handling
420/// mechanisms, including `?` operator, `Result` combinators, and error chains.
421impl std::error::Error for EvaluationError {}
422
423/// Implements the `Display` trait for user-friendly error messages.
424///
425/// Provides formatted, human-readable error messages that include error category
426/// prefixes for easy identification of error types.
427impl std::fmt::Display for EvaluationError {
428    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429        match self {
430            EvaluationError::TypeError(msg) => write!(f, "Type Error: {}", msg),
431            EvaluationError::InvalidArgument(msg) => write!(f, "Invalid Argument: {}", msg),
432            EvaluationError::UndefinedVariable(name) => write!(f, "Undefined Variable: {}", name),
433            EvaluationError::InvalidOperation(msg) => write!(f, "Invalid Operation: {}", msg),
434            EvaluationError::InvalidArity(msg) => write!(f, "Invalid Arity: {}", msg),
435            EvaluationError::InvalidIndex(msg) => write!(f, "Invalid Index: {}", msg),
436            EvaluationError::DivisionByZero => write!(f, "Division by zero"),
437            EvaluationError::ArithmeticOverflow => write!(f, "Arithmetic overflow"),
438            EvaluationError::InvalidRegex(msg) => write!(f, "Invalid Regex: {}", msg),
439            EvaluationError::InvalidTypeSpecifier(msg) => {
440                write!(f, "Invalid Type Specifier: {}", msg)
441            }
442            EvaluationError::SingletonEvaluationError(msg) => {
443                write!(f, "Singleton Evaluation Error: {}", msg)
444            }
445            EvaluationError::SemanticError(msg) => write!(f, "Semantic Error: {}", msg),
446            EvaluationError::UnsupportedFunction(msg) => write!(f, "Unsupported Function: {}", msg),
447            EvaluationError::Other(msg) => write!(f, "Evaluation Error: {}", msg),
448        }
449    }
450}
451
452// === EvaluationResult Trait Implementations ===
453
454/// Implements equality comparison for `EvaluationResult`.
455///
456/// This implementation follows FHIRPath equality semantics:
457/// - Decimal values are normalized before comparison for precision consistency
458/// - Collections compare both items and order flags
459/// - Objects use HashMap equality (order-independent)
460/// - Cross-variant comparisons always return `false`
461///
462/// # Examples
463///
464/// ```rust
465/// use helios_fhirpath_support::EvaluationResult;
466/// use rust_decimal::Decimal;
467///
468/// let a = EvaluationResult::String("test".to_string(), None);
469/// let b = EvaluationResult::String("test".to_string(), None);
470/// assert_eq!(a, b);
471///
472/// let c = EvaluationResult::Decimal(Decimal::new(100, 2), None); // 1.00
473/// let d = EvaluationResult::Decimal(Decimal::new(1, 0), None);   // 1
474/// assert_eq!(c, d); // Normalized decimals are equal
475/// ```
476impl PartialEq for EvaluationResult {
477    fn eq(&self, other: &Self) -> bool {
478        match (self, other) {
479            (EvaluationResult::Empty, EvaluationResult::Empty) => true,
480            (EvaluationResult::Boolean(a, _), EvaluationResult::Boolean(b, _)) => a == b,
481            (EvaluationResult::String(a, _), EvaluationResult::String(b, _)) => a == b,
482            (EvaluationResult::Decimal(a, _), EvaluationResult::Decimal(b, _)) => {
483                // Normalize decimals to handle precision differences (e.g., 1.0 == 1.00)
484                a.normalize() == b.normalize()
485            }
486            (EvaluationResult::Integer(a, _), EvaluationResult::Integer(b, _)) => a == b,
487            (EvaluationResult::Integer64(a, _), EvaluationResult::Integer64(b, _)) => a == b,
488            (EvaluationResult::Date(a, _), EvaluationResult::Date(b, _)) => a == b,
489            (EvaluationResult::DateTime(a, _), EvaluationResult::DateTime(b, _)) => a == b,
490            (EvaluationResult::Time(a, _), EvaluationResult::Time(b, _)) => a == b,
491            (
492                EvaluationResult::Quantity(val_a, unit_a, _),
493                EvaluationResult::Quantity(val_b, unit_b, _),
494            ) => {
495                // Quantities are equal if both value and unit match (normalized values)
496                val_a.normalize() == val_b.normalize() && unit_a == unit_b
497            }
498            (
499                EvaluationResult::Collection {
500                    items: a_items,
501                    has_undefined_order: a_undef,
502                    ..
503                },
504                EvaluationResult::Collection {
505                    items: b_items,
506                    has_undefined_order: b_undef,
507                    ..
508                },
509            ) => {
510                // Collections are equal if both order flags and items match
511                a_undef == b_undef && a_items == b_items
512            }
513            (EvaluationResult::Object { map: a, .. }, EvaluationResult::Object { map: b, .. }) => {
514                a == b
515            }
516            _ => false,
517        }
518    }
519}
520/// Marker trait implementation indicating that `EvaluationResult` has total equality.
521///
522/// Since we implement `PartialEq` with total equality semantics (no NaN-like values),
523/// we can safely implement `Eq`.
524impl Eq for EvaluationResult {}
525
526/// Implements partial ordering for `EvaluationResult`.
527///
528/// This provides a consistent ordering for sorting operations, but note that this
529/// ordering is primarily for internal use (e.g., in collections) and may not
530/// reflect FHIRPath's comparison semantics, which are handled separately.
531impl PartialOrd for EvaluationResult {
532    /// Compares two evaluation results for partial ordering.
533    ///
534    /// Since we implement total ordering, this always returns `Some`.
535    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
536        Some(self.cmp(other))
537    }
538}
539
540/// Implements total ordering for `EvaluationResult`.
541///
542/// This provides a deterministic ordering for all evaluation results, enabling
543/// their use in sorted collections. The ordering is defined by:
544/// 1. Variant precedence (Empty < Boolean < Integer < ... < Object)
545/// 2. Value comparison within the same variant
546///
547/// Note: This is an arbitrary but consistent ordering for internal use.
548/// FHIRPath comparison operators use different semantics.
549impl Ord for EvaluationResult {
550    /// Compares two evaluation results for total ordering.
551    ///
552    /// Returns the ordering relationship between `self` and `other`.
553    fn cmp(&self, other: &Self) -> Ordering {
554        match (self, other) {
555            // Order variants by type precedence
556            (EvaluationResult::Empty, EvaluationResult::Empty) => Ordering::Equal,
557            (EvaluationResult::Empty, _) => Ordering::Less,
558            (_, EvaluationResult::Empty) => Ordering::Greater,
559
560            (EvaluationResult::Boolean(a, _), EvaluationResult::Boolean(b, _)) => a.cmp(b),
561            (EvaluationResult::Boolean(_, _), _) => Ordering::Less,
562            (_, EvaluationResult::Boolean(_, _)) => Ordering::Greater,
563
564            (EvaluationResult::Integer(a, _), EvaluationResult::Integer(b, _)) => a.cmp(b),
565            (EvaluationResult::Integer(_, _), _) => Ordering::Less,
566            (_, EvaluationResult::Integer(_, _)) => Ordering::Greater,
567
568            (EvaluationResult::Integer64(a, _), EvaluationResult::Integer64(b, _)) => a.cmp(b),
569            (EvaluationResult::Integer64(_, _), _) => Ordering::Less,
570            (_, EvaluationResult::Integer64(_, _)) => Ordering::Greater,
571
572            (EvaluationResult::Decimal(a, _), EvaluationResult::Decimal(b, _)) => a.cmp(b),
573            (EvaluationResult::Decimal(_, _), _) => Ordering::Less,
574            (_, EvaluationResult::Decimal(_, _)) => Ordering::Greater,
575
576            (EvaluationResult::String(a, _), EvaluationResult::String(b, _)) => a.cmp(b),
577            (EvaluationResult::String(_, _), _) => Ordering::Less,
578            (_, EvaluationResult::String(_, _)) => Ordering::Greater,
579
580            (EvaluationResult::Date(a, _), EvaluationResult::Date(b, _)) => a.cmp(b),
581            (EvaluationResult::Date(_, _), _) => Ordering::Less,
582            (_, EvaluationResult::Date(_, _)) => Ordering::Greater,
583
584            (EvaluationResult::DateTime(a, _), EvaluationResult::DateTime(b, _)) => a.cmp(b),
585            (EvaluationResult::DateTime(_, _), _) => Ordering::Less,
586            (_, EvaluationResult::DateTime(_, _)) => Ordering::Greater,
587
588            (EvaluationResult::Time(a, _), EvaluationResult::Time(b, _)) => a.cmp(b),
589            (EvaluationResult::Time(_, _), _) => Ordering::Less,
590            (_, EvaluationResult::Time(_, _)) => Ordering::Greater,
591
592            (
593                EvaluationResult::Quantity(val_a, unit_a, _),
594                EvaluationResult::Quantity(val_b, unit_b, _),
595            ) => {
596                // Order by value first, then by unit string
597                match val_a.cmp(val_b) {
598                    Ordering::Equal => unit_a.cmp(unit_b),
599                    other => other,
600                }
601            }
602            (EvaluationResult::Quantity(_, _, _), _) => Ordering::Less,
603            (_, EvaluationResult::Quantity(_, _, _)) => Ordering::Greater,
604
605            (
606                EvaluationResult::Collection {
607                    items: a_items,
608                    has_undefined_order: a_undef,
609                    ..
610                },
611                EvaluationResult::Collection {
612                    items: b_items,
613                    has_undefined_order: b_undef,
614                    ..
615                },
616            ) => {
617                // Order by undefined_order flag first (false < true), then by items
618                match a_undef.cmp(b_undef) {
619                    Ordering::Equal => {
620                        // Compare items as ordered lists (FHIRPath collections maintain order)
621                        a_items.cmp(b_items)
622                    }
623                    other => other,
624                }
625            }
626            (EvaluationResult::Collection { .. }, _) => Ordering::Less,
627            (_, EvaluationResult::Collection { .. }) => Ordering::Greater,
628
629            (EvaluationResult::Object { map: a, .. }, EvaluationResult::Object { map: b, .. }) => {
630                // Compare objects by sorted keys, then by values
631                let mut a_keys: Vec<_> = a.keys().collect();
632                let mut b_keys: Vec<_> = b.keys().collect();
633                a_keys.sort();
634                b_keys.sort();
635
636                match a_keys.cmp(&b_keys) {
637                    Ordering::Equal => {
638                        // Same keys: compare values in sorted key order
639                        for key in a_keys {
640                            match a[key].cmp(&b[key]) {
641                                Ordering::Equal => continue,
642                                non_equal => return non_equal,
643                            }
644                        }
645                        Ordering::Equal
646                    }
647                    non_equal => non_equal,
648                }
649            } // Note: Object is the last variant, so no additional arms needed
650        }
651    }
652}
653/// Implements hashing for `EvaluationResult`.
654///
655/// This implementation enables use of `EvaluationResult` in hash-based collections
656/// like `HashSet` and `HashMap`. The hash implementation is consistent with equality:
657/// values that are equal will have the same hash.
658///
659/// # Hash Stability
660///
661/// - Decimal values are normalized before hashing for consistency
662/// - Collections hash both the items and the order flag
663/// - Objects hash keys in sorted order for deterministic results
664/// - All variants include a discriminant hash to avoid collisions
665///
666/// # Use Cases
667///
668/// This implementation enables FHIRPath operations like:
669/// - `distinct()` function using `HashSet` for deduplication
670/// - `intersect()` and `union()` set operations
671/// - Efficient lookups in evaluation contexts
672impl Hash for EvaluationResult {
673    /// Computes the hash of this evaluation result.
674    ///
675    /// The hash implementation ensures that equal values produce equal hashes
676    /// and provides good distribution for hash-based collections.
677    fn hash<H: Hasher>(&self, state: &mut H) {
678        // Hash the enum variant first to avoid cross-variant collisions
679        core::mem::discriminant(self).hash(state);
680        match self {
681            // Empty has no additional data to hash
682            EvaluationResult::Empty => {}
683            EvaluationResult::Boolean(b, _) => b.hash(state),
684            EvaluationResult::String(s, _) => s.hash(state),
685            // Hash normalized decimal for consistency with equality
686            EvaluationResult::Decimal(d, _) => d.normalize().hash(state),
687            EvaluationResult::Integer(i, _) => i.hash(state),
688            EvaluationResult::Integer64(i, _) => i.hash(state),
689            EvaluationResult::Date(d, _) => d.hash(state),
690            EvaluationResult::DateTime(dt, _) => dt.hash(state),
691            EvaluationResult::Time(t, _) => t.hash(state),
692            EvaluationResult::Quantity(val, unit, _) => {
693                // Hash both normalized value and unit
694                val.normalize().hash(state);
695                unit.hash(state);
696            }
697            EvaluationResult::Collection {
698                items,
699                has_undefined_order,
700                ..
701            } => {
702                // Hash order flag and items
703                has_undefined_order.hash(state);
704                items.len().hash(state);
705                for item in items {
706                    item.hash(state);
707                }
708            }
709            EvaluationResult::Object { map, .. } => {
710                // Hash objects with sorted keys for deterministic results
711                // Note: We don't hash type_namespace/type_name to maintain compatibility
712                let mut keys: Vec<_> = map.keys().collect();
713                keys.sort();
714                keys.len().hash(state);
715                for key in keys {
716                    key.hash(state);
717                    map[key].hash(state);
718                }
719            }
720        }
721    }
722}
723
724// === EvaluationResult Methods ===
725
726impl EvaluationResult {
727    // === Constructor Methods ===
728
729    /// Creates a Boolean result with System type.
730    pub fn boolean(value: bool) -> Self {
731        EvaluationResult::Boolean(value, Some(TypeInfoResult::new("System", "Boolean")))
732    }
733
734    /// Creates a Boolean result with FHIR type.
735    pub fn fhir_boolean(value: bool) -> Self {
736        EvaluationResult::Boolean(value, Some(TypeInfoResult::new("FHIR", "boolean")))
737    }
738
739    /// Creates a String result with System type.
740    pub fn string(value: String) -> Self {
741        EvaluationResult::String(value, Some(TypeInfoResult::new("System", "String")))
742    }
743
744    /// Creates a String result with FHIR type.
745    pub fn fhir_string(value: String, fhir_type: &str) -> Self {
746        EvaluationResult::String(value, Some(TypeInfoResult::new("FHIR", fhir_type)))
747    }
748
749    /// Creates an Integer result with System type.
750    pub fn integer(value: i64) -> Self {
751        EvaluationResult::Integer(value, Some(TypeInfoResult::new("System", "Integer")))
752    }
753
754    /// Creates an Integer result with FHIR type.
755    pub fn fhir_integer(value: i64) -> Self {
756        EvaluationResult::Integer(value, Some(TypeInfoResult::new("FHIR", "integer")))
757    }
758
759    /// Creates an Integer64 result with System type.
760    pub fn integer64(value: i64) -> Self {
761        EvaluationResult::Integer64(value, Some(TypeInfoResult::new("System", "Integer64")))
762    }
763
764    /// Creates an Integer64 result with FHIR type.
765    pub fn fhir_integer64(value: i64) -> Self {
766        EvaluationResult::Integer64(value, Some(TypeInfoResult::new("FHIR", "integer64")))
767    }
768
769    /// Creates a Decimal result with System type.
770    pub fn decimal(value: Decimal) -> Self {
771        EvaluationResult::Decimal(value, Some(TypeInfoResult::new("System", "Decimal")))
772    }
773
774    /// Creates a Decimal result with FHIR type.
775    pub fn fhir_decimal(value: Decimal) -> Self {
776        EvaluationResult::Decimal(value, Some(TypeInfoResult::new("FHIR", "decimal")))
777    }
778
779    /// Creates a Date result with System type.
780    pub fn date(value: String) -> Self {
781        EvaluationResult::Date(value, Some(TypeInfoResult::new("System", "Date")))
782    }
783
784    /// Creates a DateTime result with System type.
785    pub fn datetime(value: String) -> Self {
786        EvaluationResult::DateTime(value, Some(TypeInfoResult::new("System", "DateTime")))
787    }
788
789    /// Creates a Time result with System type.
790    pub fn time(value: String) -> Self {
791        EvaluationResult::Time(value, Some(TypeInfoResult::new("System", "Time")))
792    }
793
794    /// Creates a Quantity result with System type.
795    pub fn quantity(value: Decimal, unit: String) -> Self {
796        EvaluationResult::Quantity(value, unit, Some(TypeInfoResult::new("System", "Quantity")))
797    }
798
799    /// Creates a Collection result.
800    pub fn collection(items: Vec<EvaluationResult>) -> Self {
801        EvaluationResult::Collection {
802            items,
803            has_undefined_order: false,
804            type_info: None,
805        }
806    }
807
808    /// Creates an Object variant with just the map, no type information.
809    pub fn object(map: HashMap<String, EvaluationResult>) -> Self {
810        EvaluationResult::Object {
811            map,
812            type_info: None,
813        }
814    }
815
816    /// Creates an Object variant with type information.
817    pub fn typed_object(
818        map: HashMap<String, EvaluationResult>,
819        type_namespace: &str,
820        type_name: &str,
821    ) -> Self {
822        EvaluationResult::Object {
823            map,
824            type_info: Some(TypeInfoResult::new(type_namespace, type_name)),
825        }
826    }
827
828    // === Value Extraction Methods ===
829
830    /// Extracts the boolean value if this is a Boolean variant.
831    pub fn as_boolean(&self) -> Option<bool> {
832        match self {
833            EvaluationResult::Boolean(val, _) => Some(*val),
834            _ => None,
835        }
836    }
837
838    /// Extracts the string value if this is a String variant.
839    pub fn as_string(&self) -> Option<&String> {
840        match self {
841            EvaluationResult::String(val, _) => Some(val),
842            _ => None,
843        }
844    }
845
846    /// Extracts the integer value if this is an Integer variant.
847    pub fn as_integer(&self) -> Option<i64> {
848        match self {
849            EvaluationResult::Integer(val, _) => Some(*val),
850            _ => None,
851        }
852    }
853
854    /// Extracts the integer value if this is an Integer64 variant.
855    pub fn as_integer64(&self) -> Option<i64> {
856        match self {
857            EvaluationResult::Integer64(val, _) => Some(*val),
858            _ => None,
859        }
860    }
861
862    /// Extracts the decimal value if this is a Decimal variant.
863    pub fn as_decimal(&self) -> Option<Decimal> {
864        match self {
865            EvaluationResult::Decimal(val, _) => Some(*val),
866            _ => None,
867        }
868    }
869
870    /// Extracts the date value if this is a Date variant.
871    pub fn as_date(&self) -> Option<&String> {
872        match self {
873            EvaluationResult::Date(val, _) => Some(val),
874            _ => None,
875        }
876    }
877
878    /// Extracts the datetime value if this is a DateTime variant.
879    pub fn as_datetime(&self) -> Option<&String> {
880        match self {
881            EvaluationResult::DateTime(val, _) => Some(val),
882            _ => None,
883        }
884    }
885
886    /// Extracts the time value if this is a Time variant.
887    pub fn as_time(&self) -> Option<&String> {
888        match self {
889            EvaluationResult::Time(val, _) => Some(val),
890            _ => None,
891        }
892    }
893
894    /// Extracts the quantity value if this is a Quantity variant.
895    pub fn as_quantity(&self) -> Option<(Decimal, &String)> {
896        match self {
897            EvaluationResult::Quantity(val, unit, _) => Some((*val, unit)),
898            _ => None,
899        }
900    }
901    /// Checks if this result represents a collection.
902    ///
903    /// Returns `true` only for the `Collection` variant, not for other
904    /// multi-valued representations like `Object`.
905    ///
906    /// # Examples
907    ///
908    /// ```rust
909    /// use helios_fhirpath_support::EvaluationResult;
910    ///
911    /// let collection = EvaluationResult::Collection {
912    ///     items: vec![],
913    ///     has_undefined_order: false,
914    ///     type_info: None,
915    /// };
916    /// assert!(collection.is_collection());
917    ///
918    /// let string = EvaluationResult::String("test".to_string(), None);
919    /// assert!(!string.is_collection());
920    /// ```
921    pub fn is_collection(&self) -> bool {
922        matches!(self, EvaluationResult::Collection { .. })
923    }
924
925    /// Returns the count of items according to FHIRPath counting rules.
926    ///
927    /// FHIRPath counting semantics:
928    /// - `Empty`: 0 items
929    /// - `Collection`: number of items in the collection
930    /// - All other variants: 1 item (single values)
931    ///
932    /// This matches the behavior of FHIRPath's `count()` function.
933    ///
934    /// # Examples
935    ///
936    /// ```rust
937    /// use helios_fhirpath_support::EvaluationResult;
938    ///
939    /// assert_eq!(EvaluationResult::Empty.count(), 0);
940    /// assert_eq!(EvaluationResult::String("test".to_string(), None).count(), 1);
941    ///
942    /// let collection = EvaluationResult::Collection {
943    ///     items: vec![
944    ///         EvaluationResult::Integer(1, None),
945    ///         EvaluationResult::Integer(2, None),
946    ///     ],
947    ///     has_undefined_order: false,
948    ///     type_info: None,
949    /// };
950    /// assert_eq!(collection.count(), 2);
951    /// ```
952    pub fn count(&self) -> usize {
953        match self {
954            EvaluationResult::Empty => 0,
955            EvaluationResult::Collection { items, .. } => items.len(),
956            _ => 1, // All non-collection variants count as 1
957        }
958    }
959    /// Converts the result to a boolean value according to FHIRPath truthiness rules.
960    ///
961    /// FHIRPath truthiness semantics:
962    /// - `Empty`: `false`
963    /// - `Boolean`: the boolean value itself
964    /// - `String`: `false` if empty, `true` otherwise
965    /// - `Decimal`/`Integer`: `false` if zero, `true` otherwise
966    /// - `Quantity`: `false` if value is zero, `true` otherwise
967    /// - `Collection`: `false` if empty, `true` otherwise
968    /// - Other types: `true` (Date, DateTime, Time, Object)
969    ///
970    /// Note: This is different from boolean conversion for logical operators.
971    ///
972    /// # Examples
973    ///
974    /// ```rust
975    /// use helios_fhirpath_support::EvaluationResult;
976    /// use rust_decimal::Decimal;
977    ///
978    /// assert_eq!(EvaluationResult::Empty.to_boolean(), false);
979    /// assert_eq!(EvaluationResult::Boolean(true, None).to_boolean(), true);
980    /// assert_eq!(EvaluationResult::String("".to_string(), None).to_boolean(), false);
981    /// assert_eq!(EvaluationResult::String("text".to_string(), None).to_boolean(), true);
982    /// assert_eq!(EvaluationResult::Integer(0, None).to_boolean(), false);
983    /// assert_eq!(EvaluationResult::Integer(42, None).to_boolean(), true);
984    /// ```
985    pub fn to_boolean(&self) -> bool {
986        match self {
987            EvaluationResult::Empty => false,
988            EvaluationResult::Boolean(b, _) => *b,
989            EvaluationResult::String(s, _) => !s.is_empty(),
990            EvaluationResult::Decimal(d, _) => !d.is_zero(),
991            EvaluationResult::Integer(i, _) => *i != 0,
992            EvaluationResult::Integer64(i, _) => *i != 0,
993            EvaluationResult::Quantity(q, _, _) => !q.is_zero(), // Truthy if value is non-zero
994            EvaluationResult::Collection { items, .. } => !items.is_empty(),
995            _ => true, // Date, DateTime, Time, Object are always truthy
996        }
997    }
998
999    /// Converts the result to its string representation.
1000    ///
1001    /// This method provides the string representation used by FHIRPath's
1002    /// `toString()` function and string conversion operations.
1003    ///
1004    /// # Conversion Rules
1005    ///
1006    /// - `Empty`: empty string
1007    /// - `Boolean`: "true" or "false"
1008    /// - `String`: the string value itself
1009    /// - Numeric types: string representation of the number
1010    /// - Date/Time types: the ISO format string
1011    /// - `Quantity`: formatted as "value 'unit'"
1012    /// - `Collection`: if single item, its string value; otherwise bracketed list
1013    /// - `Object`: "\[object\]" placeholder
1014    ///
1015    /// # Examples
1016    ///
1017    /// ```rust
1018    /// use helios_fhirpath_support::EvaluationResult;
1019    /// use rust_decimal::Decimal;
1020    ///
1021    /// assert_eq!(EvaluationResult::Empty.to_string_value(), "");
1022    /// assert_eq!(EvaluationResult::Boolean(true, None).to_string_value(), "true");
1023    /// assert_eq!(EvaluationResult::Integer(42, None).to_string_value(), "42");
1024    ///
1025    /// let quantity = EvaluationResult::Quantity(Decimal::new(54, 1), "mg".to_string(), None);
1026    /// assert_eq!(quantity.to_string_value(), "5.4 'mg'");
1027    /// ```
1028    pub fn to_string_value(&self) -> String {
1029        match self {
1030            EvaluationResult::Empty => "".to_string(),
1031            EvaluationResult::Boolean(b, _) => b.to_string(),
1032            EvaluationResult::String(s, _) => s.clone(),
1033            EvaluationResult::Decimal(d, _) => d.to_string(),
1034            EvaluationResult::Integer(i, _) => i.to_string(),
1035            EvaluationResult::Integer64(i, _) => i.to_string(),
1036            EvaluationResult::Date(d, _) => d.clone(), // Return stored string
1037            EvaluationResult::DateTime(dt, _) => dt.clone(), // Return stored string
1038            EvaluationResult::Time(t, _) => t.clone(), // Return stored string
1039            EvaluationResult::Quantity(val, unit, _) => {
1040                // Format as "value unit" for toString()
1041                // The FHIRPath spec for toString() doesn't require quotes around the unit
1042                let formatted_unit = format_unit_for_display(unit);
1043                format!("{} {}", val, formatted_unit)
1044            }
1045            EvaluationResult::Collection { items, .. } => {
1046                // FHIRPath toString rules for collections
1047                if items.len() == 1 {
1048                    // Single item: return its string value
1049                    items[0].to_string_value()
1050                } else {
1051                    // Multiple items: return bracketed comma-separated list
1052                    format!(
1053                        "[{}]",
1054                        items
1055                            .iter()
1056                            .map(|r| r.to_string_value())
1057                            .collect::<Vec<_>>()
1058                            .join(", ")
1059                    )
1060                }
1061            }
1062            EvaluationResult::Object { .. } => "[object]".to_string(),
1063        }
1064    }
1065
1066    /// Converts the result to Boolean for logical operators (and, or, xor, implies).
1067    ///
1068    /// This method implements the specific boolean conversion rules used by FHIRPath
1069    /// logical operators, which are different from general truthiness rules.
1070    ///
1071    /// # Conversion Rules
1072    ///
1073    /// - `Boolean`: returns the boolean value unchanged
1074    /// - `String`: converts "true"/"t"/"yes"/"1"/"1.0" to `true`,
1075    ///   "false"/"f"/"no"/"0"/"0.0" to `false`, others to `Empty`
1076    /// - `Collection`: single items are recursively converted, empty becomes `Empty`,
1077    ///   multiple items cause an error
1078    /// - Other types: result in `Empty`
1079    ///
1080    /// # Errors
1081    ///
1082    /// Returns `SingletonEvaluationError` if called on a collection with multiple items.
1083    ///
1084    /// # Examples
1085    ///
1086    /// ```rust
1087    /// use helios_fhirpath_support::{EvaluationResult, EvaluationError};
1088    ///
1089    /// let true_str = EvaluationResult::String("true".to_string(), None);
1090    /// assert_eq!(true_str.to_boolean_for_logic().unwrap(), EvaluationResult::Boolean(true, None));
1091    ///
1092    /// let false_str = EvaluationResult::String("false".to_string(), None);
1093    /// assert_eq!(false_str.to_boolean_for_logic().unwrap(), EvaluationResult::Boolean(false, None));
1094    ///
1095    /// let other_str = EvaluationResult::String("maybe".to_string(), None);
1096    /// assert_eq!(other_str.to_boolean_for_logic().unwrap(), EvaluationResult::Empty);
1097    ///
1098    /// let integer = EvaluationResult::Integer(42, None);
1099    /// assert_eq!(integer.to_boolean_for_logic().unwrap(), EvaluationResult::Boolean(true, None));
1100    /// ```
1101    pub fn to_boolean_for_logic(&self) -> Result<EvaluationResult, EvaluationError> {
1102        // Default to R5 behavior for backward compatibility
1103        self.to_boolean_for_logic_with_r4_compat(false)
1104    }
1105
1106    /// Converts this evaluation result to its boolean representation for logical operations
1107    /// with R4 compatibility mode for integer handling
1108    ///
1109    /// # Arguments
1110    /// * `r4_compat` - If true, uses R4 semantics where 0 is false and non-zero is true.
1111    ///                 If false, uses R5+ semantics where all integers are truthy.
1112    pub fn to_boolean_for_logic_with_r4_compat(
1113        &self,
1114        r4_compat: bool,
1115    ) -> Result<EvaluationResult, EvaluationError> {
1116        match self {
1117            EvaluationResult::Boolean(b, type_info) => {
1118                Ok(EvaluationResult::Boolean(*b, type_info.clone()))
1119            }
1120            EvaluationResult::String(s, _) => {
1121                // Convert string to boolean based on recognized values
1122                Ok(match s.to_lowercase().as_str() {
1123                    "true" | "t" | "yes" | "1" | "1.0" => EvaluationResult::boolean(true),
1124                    "false" | "f" | "no" | "0" | "0.0" => EvaluationResult::boolean(false),
1125                    _ => EvaluationResult::Empty, // Unrecognized strings become Empty
1126                })
1127            }
1128            EvaluationResult::Collection { items, .. } => {
1129                match items.len() {
1130                    0 => Ok(EvaluationResult::Empty),
1131                    1 => items[0].to_boolean_for_logic_with_r4_compat(r4_compat), // Recursive conversion
1132                    n => Err(EvaluationError::SingletonEvaluationError(format!(
1133                        "Boolean logic requires singleton collection, found {} items",
1134                        n
1135                    ))),
1136                }
1137            }
1138            EvaluationResult::Integer(i, _) => {
1139                if r4_compat {
1140                    // R4/R4B: C-like semantics - 0 is false, non-zero is true
1141                    Ok(EvaluationResult::boolean(*i != 0))
1142                } else {
1143                    // R5/R6: All integers are truthy (even 0)
1144                    Ok(EvaluationResult::boolean(true))
1145                }
1146            }
1147            EvaluationResult::Integer64(i, _) => {
1148                if r4_compat {
1149                    // R4/R4B: C-like semantics - 0 is false, non-zero is true
1150                    Ok(EvaluationResult::boolean(*i != 0))
1151                } else {
1152                    // R5/R6: All integers are truthy (even 0)
1153                    Ok(EvaluationResult::boolean(true))
1154                }
1155            }
1156            // Per FHIRPath spec section 5.2: other types evaluate to Empty for logical operators
1157            EvaluationResult::Decimal(_, _)
1158            | EvaluationResult::Date(_, _)
1159            | EvaluationResult::DateTime(_, _)
1160            | EvaluationResult::Time(_, _)
1161            | EvaluationResult::Quantity(_, _, _)
1162            | EvaluationResult::Object { .. } => Ok(EvaluationResult::Empty),
1163            EvaluationResult::Empty => Ok(EvaluationResult::Empty),
1164        }
1165    }
1166
1167    /// Checks if the result is a String or Empty variant.
1168    ///
1169    /// This is a utility method used in various FHIRPath operations that
1170    /// need to distinguish string-like values from other types.
1171    ///
1172    /// # Examples
1173    ///
1174    /// ```rust
1175    /// use helios_fhirpath_support::EvaluationResult;
1176    ///
1177    /// assert!(EvaluationResult::Empty.is_string_or_empty());
1178    /// assert!(EvaluationResult::String("test".to_string(), None).is_string_or_empty());
1179    /// assert!(!EvaluationResult::Integer(42, None).is_string_or_empty());
1180    /// ```
1181    pub fn is_string_or_empty(&self) -> bool {
1182        matches!(
1183            self,
1184            EvaluationResult::String(_, _) | EvaluationResult::Empty
1185        )
1186    }
1187
1188    /// Returns the type name of this evaluation result.
1189    ///
1190    /// This method returns a string representation of the variant type,
1191    /// useful for error messages, debugging, and type checking operations.
1192    ///
1193    /// # Examples
1194    ///
1195    /// ```rust
1196    /// use helios_fhirpath_support::EvaluationResult;
1197    ///
1198    /// assert_eq!(EvaluationResult::Empty.type_name(), "Empty");
1199    /// assert_eq!(EvaluationResult::String("test".to_string(), None).type_name(), "String");
1200    /// assert_eq!(EvaluationResult::Integer(42, None).type_name(), "Integer");
1201    ///
1202    /// let collection = EvaluationResult::Collection {
1203    ///     items: vec![],
1204    ///     has_undefined_order: false,
1205    ///     type_info: None,
1206    /// };
1207    /// assert_eq!(collection.type_name(), "Collection");
1208    /// ```
1209    pub fn type_name(&self) -> &'static str {
1210        match self {
1211            EvaluationResult::Empty => "Empty",
1212            EvaluationResult::Boolean(_, _) => "Boolean",
1213            EvaluationResult::String(_, _) => "String",
1214            EvaluationResult::Decimal(_, _) => "Decimal",
1215            EvaluationResult::Integer(_, _) => "Integer",
1216            EvaluationResult::Integer64(_, _) => "Integer64",
1217            EvaluationResult::Date(_, _) => "Date",
1218            EvaluationResult::DateTime(_, _) => "DateTime",
1219            EvaluationResult::Time(_, _) => "Time",
1220            EvaluationResult::Quantity(_, _, _) => "Quantity",
1221            EvaluationResult::Collection { .. } => "Collection",
1222            EvaluationResult::Object { .. } => "Object",
1223        }
1224    }
1225}
1226
1227// === IntoEvaluationResult Implementations ===
1228//
1229// The following implementations provide conversions from standard Rust types
1230// and common patterns into EvaluationResult variants. These enable seamless
1231// integration between Rust code and the FHIRPath evaluation system.
1232
1233/// Converts a `String` to `EvaluationResult::String`.
1234///
1235/// This is the most direct conversion for text values in the FHIRPath system.
1236impl IntoEvaluationResult for String {
1237    fn to_evaluation_result(&self) -> EvaluationResult {
1238        EvaluationResult::string(self.clone())
1239    }
1240}
1241
1242/// Converts a `bool` to `EvaluationResult::Boolean`.
1243///
1244/// Enables direct use of Rust boolean values in FHIRPath expressions.
1245impl IntoEvaluationResult for bool {
1246    fn to_evaluation_result(&self) -> EvaluationResult {
1247        EvaluationResult::boolean(*self)
1248    }
1249}
1250
1251/// Converts an `i32` to `EvaluationResult::Integer`.
1252///
1253/// Automatically promotes to `i64` for consistent integer handling.
1254impl IntoEvaluationResult for i32 {
1255    fn to_evaluation_result(&self) -> EvaluationResult {
1256        EvaluationResult::integer(*self as i64)
1257    }
1258}
1259
1260/// Converts an `i64` to `EvaluationResult::Integer`.
1261///
1262/// This is the primary integer type used in FHIRPath evaluation.
1263impl IntoEvaluationResult for i64 {
1264    fn to_evaluation_result(&self) -> EvaluationResult {
1265        EvaluationResult::integer64(*self)
1266    }
1267}
1268
1269/// Converts an `f64` to `EvaluationResult::Decimal` with error handling.
1270///
1271/// Uses high-precision `Decimal` type to avoid floating-point errors.
1272/// Returns `Empty` for invalid values like NaN or Infinity.
1273impl IntoEvaluationResult for f64 {
1274    fn to_evaluation_result(&self) -> EvaluationResult {
1275        Decimal::from_f64(*self)
1276            .map(EvaluationResult::decimal)
1277            .unwrap_or(EvaluationResult::Empty)
1278    }
1279}
1280
1281/// Converts a `rust_decimal::Decimal` to `EvaluationResult::Decimal`.
1282///
1283/// This is the preferred conversion for precise decimal values in FHIR.
1284impl IntoEvaluationResult for Decimal {
1285    fn to_evaluation_result(&self) -> EvaluationResult {
1286        EvaluationResult::decimal(*self)
1287    }
1288}
1289
1290// === Generic Container Implementations ===
1291//
1292// These implementations handle common Rust container types, enabling
1293// seamless conversion of complex data structures to FHIRPath results.
1294
1295/// Converts `Option<T>` to either the inner value's result or `Empty`.
1296///
1297/// This is fundamental for handling FHIR's optional fields and nullable values.
1298/// `Some(value)` converts the inner value, `None` becomes `Empty`.
1299impl<T> IntoEvaluationResult for Option<T>
1300where
1301    T: IntoEvaluationResult,
1302{
1303    fn to_evaluation_result(&self) -> EvaluationResult {
1304        match self {
1305            Some(value) => value.to_evaluation_result(),
1306            None => EvaluationResult::Empty,
1307        }
1308    }
1309}
1310
1311/// Converts `Vec<T>` to `EvaluationResult::Collection`.
1312///
1313/// Each item in the vector is converted to an `EvaluationResult`. The resulting
1314/// collection is marked as having defined order (FHIRPath collections maintain order).
1315impl<T> IntoEvaluationResult for Vec<T>
1316where
1317    T: IntoEvaluationResult,
1318{
1319    fn to_evaluation_result(&self) -> EvaluationResult {
1320        let collection: Vec<EvaluationResult> = self
1321            .iter()
1322            .map(|item| item.to_evaluation_result())
1323            .collect();
1324        EvaluationResult::Collection {
1325            items: collection,
1326            has_undefined_order: false,
1327            type_info: None,
1328        }
1329    }
1330}
1331
1332/// Converts `Box<T>` to the result of the boxed value.
1333///
1334/// This enables use of boxed values (often used to break circular references
1335/// in FHIR data structures) directly in FHIRPath evaluation.
1336impl<T> IntoEvaluationResult for Box<T>
1337where
1338    T: IntoEvaluationResult + ?Sized,
1339{
1340    fn to_evaluation_result(&self) -> EvaluationResult {
1341        (**self).to_evaluation_result()
1342    }
1343}
1344
1345/// Convenience function for converting values to evaluation results.
1346///
1347/// This function provides a unified interface for conversion that can be used
1348/// by the evaluator and macro systems. It's particularly useful when working
1349/// with trait objects or in generic contexts.
1350///
1351/// # Arguments
1352///
1353/// * `value` - Any value implementing `IntoEvaluationResult`
1354///
1355/// # Returns
1356///
1357/// The `EvaluationResult` representation of the input value.
1358///
1359/// # Examples
1360///
1361/// ```rust
1362/// use helios_fhirpath_support::{convert_value_to_evaluation_result, EvaluationResult};
1363///
1364/// let result = convert_value_to_evaluation_result(&"hello".to_string());
1365/// assert_eq!(result, EvaluationResult::String("hello".to_string(), None));
1366///
1367/// let numbers = vec![1, 2, 3];
1368/// let collection_result = convert_value_to_evaluation_result(&numbers);
1369/// assert_eq!(collection_result.count(), 3);
1370/// ```
1371pub fn convert_value_to_evaluation_result<T>(value: &T) -> EvaluationResult
1372where
1373    T: IntoEvaluationResult + ?Sized,
1374{
1375    value.to_evaluation_result()
1376}
1377
1378/// Formats a unit for display in toString() output
1379fn format_unit_for_display(unit: &str) -> String {
1380    // FHIRPath spec formatting for units in toString():
1381    // - Calendar word units (week, day, etc.): displayed without quotes
1382    // - UCUM code units ('wk', 'mg', etc.): displayed with quotes
1383
1384    // Calendar word units that don't need quotes
1385    const CALENDAR_WORDS: &[&str] = &[
1386        "year",
1387        "years",
1388        "month",
1389        "months",
1390        "week",
1391        "weeks",
1392        "day",
1393        "days",
1394        "hour",
1395        "hours",
1396        "minute",
1397        "minutes",
1398        "second",
1399        "seconds",
1400        "millisecond",
1401        "milliseconds",
1402    ];
1403
1404    if CALENDAR_WORDS.contains(&unit) {
1405        // Calendar word units: display without quotes (R5 behavior, likely correct)
1406        unit.to_string()
1407    } else {
1408        // UCUM code units: display with quotes
1409        format!("'{}'", unit)
1410    }
1411}