helios_fhir/
lib.rs

1//! # FHIR Model Infrastructure
2//!
3//! This module provides the foundational types and infrastructure that support the
4//! generated FHIR specification implementations. It contains hand-coded types that
5//! enable the generated code to handle FHIR's complex requirements for precision,
6//! extensions, and cross-version compatibility.
7
8//!
9//! ## Architecture
10//!
11//! The FHIR crate is organized as follows:
12//! - **Generated modules** (`r4.rs`, `r4b.rs`, `r5.rs`, `r6.rs`): Complete FHIR type implementations
13//! - **Infrastructure module** (`lib.rs`): Foundational types used by generated code
14//! - **Test modules**: Validation against official FHIR examples
15//!
16//! ## Key Infrastructure Types
17//!
18//! - [`PreciseDecimal`] - High-precision decimal arithmetic preserving original string format
19//! - [`Element<T, Extension>`] - Base container for FHIR elements with extension support
20//! - [`DecimalElement<Extension>`] - Specialized element for decimal values
21//! - [`FhirVersion`] - Version enumeration for multi-version support
22//!
23//! ## Usage Example
24//!
25//! ```rust
26//! use helios_fhir::r4::{Patient, HumanName};
27//! use helios_fhir::PreciseDecimal;
28//! use rust_decimal::Decimal;
29//!
30//! // Create a patient with precise decimal handling
31//! let patient = Patient {
32//!     name: Some(vec![HumanName {
33//!         family: Some("Doe".to_string().into()),
34//!         given: Some(vec!["John".to_string().into()]),
35//!         ..Default::default()
36//!     }]),
37//!     ..Default::default()
38//! };
39//!
40//! // Work with precise decimals
41//! let precise = PreciseDecimal::from(Decimal::new(12340, 3)); // 12.340
42//! ```
43
44use chrono::{DateTime as ChronoDateTime, NaiveDate, NaiveTime, Utc};
45use helios_fhirpath_support::{EvaluationResult, IntoEvaluationResult, TypeInfoResult};
46use rust_decimal::Decimal;
47use serde::{
48    Deserialize, Serialize,
49    de::{self, Deserializer, MapAccess, Visitor},
50    ser::{SerializeStruct, Serializer},
51};
52use std::cmp::Ordering;
53use std::fmt;
54use std::marker::PhantomData;
55use std::sync::Arc;
56
57/// Custom deserializer that is more forgiving of null values in JSON.
58///
59/// This creates a custom `Option<T>` deserializer that will return None for null values
60/// but also for any deserialization errors. This makes it possible to skip over
61/// malformed or unexpected values in FHIR JSON.
62pub fn deserialize_forgiving_option<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
63where
64    T: Deserialize<'de>,
65    D: Deserializer<'de>,
66{
67    // Use the intermediate Value approach to check for null first
68    let json_value = serde_json::Value::deserialize(deserializer)?;
69
70    match json_value {
71        serde_json::Value::Null => Ok(None),
72        _ => {
73            // Try to deserialize the value, but return None if it fails
74            match T::deserialize(json_value) {
75                Ok(value) => Ok(Some(value)),
76                Err(_) => Ok(None), // Ignore errors and return None
77            }
78        }
79    }
80}
81
82/// High-precision decimal type that preserves original string representation.
83///
84/// FHIR requires that decimal values maintain their original precision and format
85/// when serialized back to JSON. This type stores both the parsed `Decimal` value
86/// for mathematical operations and the original string for serialization.
87///
88/// # FHIR Precision Requirements
89///
90/// FHIR decimal values must:
91/// - Preserve trailing zeros (e.g., "12.340" vs "12.34")
92/// - Maintain original precision during round-trip serialization
93/// - Support high-precision arithmetic without floating-point errors
94/// - Handle edge cases like very large or very small numbers
95///
96/// # Examples
97///
98/// ```rust
99/// use helios_fhir::PreciseDecimal;
100/// use rust_decimal::Decimal;
101///
102/// // Create from Decimal (derives string representation)
103/// let precise = PreciseDecimal::from(Decimal::new(12340, 3)); // 12.340
104/// assert_eq!(precise.original_string(), "12.340");
105///
106/// // Create with specific string format
107/// let precise = PreciseDecimal::from_parts(
108///     Some(Decimal::new(1000, 2)),
109///     "10.00".to_string()
110/// );
111/// assert_eq!(precise.original_string(), "10.00");
112/// ```
113#[derive(Debug, Clone)]
114pub struct PreciseDecimal {
115    /// The parsed decimal value, `None` if parsing failed (e.g., out of range)
116    value: Option<Decimal>,
117    /// The original string representation preserving format and precision
118    original_string: Arc<str>,
119}
120
121/// Implements equality comparison based on the parsed decimal value.
122///
123/// Two `PreciseDecimal` values are equal if their parsed `Decimal` values are equal,
124/// regardless of their original string representations. This enables mathematical
125/// equality while preserving string format for serialization.
126///
127/// # Examples
128///
129/// ```rust
130/// use helios_fhir::PreciseDecimal;
131/// use rust_decimal::Decimal;
132///
133/// let a = PreciseDecimal::from_parts(Some(Decimal::new(100, 1)), "10.0".to_string());
134/// let b = PreciseDecimal::from_parts(Some(Decimal::new(1000, 2)), "10.00".to_string());
135/// assert_eq!(a, b); // Same decimal value (10.0 == 10.00)
136/// ```
137impl PartialEq for PreciseDecimal {
138    fn eq(&self, other: &Self) -> bool {
139        // Compare parsed decimal values for mathematical equality
140        self.value == other.value
141    }
142}
143
144/// Marker trait implementation indicating total equality for `PreciseDecimal`.
145impl Eq for PreciseDecimal {}
146
147/// Implements partial ordering based on the parsed decimal value.
148///
149/// Ordering is based on the mathematical value of the decimal, not the string
150/// representation. `None` values (unparseable decimals) are considered less than
151/// any valid decimal value.
152impl PartialOrd for PreciseDecimal {
153    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
154        Some(self.cmp(other))
155    }
156}
157
158/// Implements total ordering for `PreciseDecimal`.
159///
160/// Provides a consistent ordering for sorting operations. The ordering is based
161/// on the mathematical value: `None` < `Some(smaller_decimal)` < `Some(larger_decimal)`.
162impl Ord for PreciseDecimal {
163    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
164        self.value.cmp(&other.value)
165    }
166}
167
168// === PreciseDecimal Methods ===
169
170impl PreciseDecimal {
171    /// Creates a new `PreciseDecimal` from its constituent parts.
172    ///
173    /// This constructor allows explicit control over both the parsed value and the
174    /// original string representation. Use this when you need to preserve a specific
175    /// string format or when parsing has already been attempted.
176    ///
177    /// # Arguments
178    ///
179    /// * `value` - The parsed decimal value, or `None` if parsing failed
180    /// * `original_string` - The original string representation to preserve
181    ///
182    /// # Examples
183    ///
184    /// ```rust
185    /// use helios_fhir::PreciseDecimal;
186    /// use rust_decimal::Decimal;
187    ///
188    /// // Create with successful parsing
189    /// let precise = PreciseDecimal::from_parts(
190    ///     Some(Decimal::new(12340, 3)),
191    ///     "12.340".to_string()
192    /// );
193    ///
194    /// // Create with failed parsing (preserves original string)
195    /// let invalid = PreciseDecimal::from_parts(
196    ///     None,
197    ///     "invalid_decimal".to_string()
198    /// );
199    /// ```
200    pub fn from_parts(value: Option<Decimal>, original_string: String) -> Self {
201        Self {
202            value,
203            original_string: Arc::from(original_string.as_str()),
204        }
205    }
206
207    /// Helper method to parse a decimal string with support for scientific notation.
208    ///
209    /// This method handles the complexity of parsing decimal strings that may be in
210    /// scientific notation (with 'E' or 'e' exponents) or regular decimal format.
211    /// It normalizes 'E' to 'e' for consistent parsing while preserving the original
212    /// string representation for serialization.
213    ///
214    /// # Arguments
215    ///
216    /// * `s` - The string to parse as a decimal
217    ///
218    /// # Returns
219    ///
220    /// `Some(Decimal)` if parsing succeeds, `None` if the string is not a valid decimal.
221    ///
222    /// # Examples
223    ///
224    /// ```ignore
225    /// use helios_fhir::PreciseDecimal;
226    /// use rust_decimal::Decimal;
227    ///
228    /// // Regular decimal format
229    /// assert!(PreciseDecimal::parse_decimal_string("123.45").is_some());
230    ///
231    /// // Scientific notation with 'e'
232    /// assert!(PreciseDecimal::parse_decimal_string("1.23e2").is_some());
233    ///
234    /// // Scientific notation with 'E' (normalized to 'e')
235    /// assert!(PreciseDecimal::parse_decimal_string("1.23E2").is_some());
236    ///
237    /// // Invalid format
238    /// assert!(PreciseDecimal::parse_decimal_string("invalid").is_none());
239    /// ```
240    fn parse_decimal_string(s: &str) -> Option<Decimal> {
241        // Normalize 'E' to 'e' for consistent parsing
242        let normalized = s.replace('E', "e");
243
244        if normalized.contains('e') {
245            // Use scientific notation parsing
246            Decimal::from_scientific(&normalized).ok()
247        } else {
248            // Use regular decimal parsing
249            normalized.parse::<Decimal>().ok()
250        }
251    }
252
253    /// Returns the parsed decimal value if parsing was successful.
254    ///
255    /// This method provides access to the mathematical value for arithmetic
256    /// operations and comparisons. Returns `None` if the original string
257    /// could not be parsed as a valid decimal.
258    ///
259    /// # Examples
260    ///
261    /// ```rust
262    /// use helios_fhir::PreciseDecimal;
263    /// use rust_decimal::Decimal;
264    ///
265    /// let precise = PreciseDecimal::from(Decimal::new(1234, 2)); // 12.34
266    /// assert_eq!(precise.value(), Some(Decimal::new(1234, 2)));
267    ///
268    /// let invalid = PreciseDecimal::from_parts(None, "invalid".to_string());
269    /// assert_eq!(invalid.value(), None);
270    /// ```
271    pub fn value(&self) -> Option<Decimal> {
272        self.value
273    }
274
275    /// Returns the original string representation.
276    ///
277    /// This method provides access to the exact string format that was used
278    /// to create this `PreciseDecimal`. This string is used during serialization
279    /// to maintain FHIR's precision requirements.
280    ///
281    /// # Examples
282    ///
283    /// ```rust
284    /// use helios_fhir::PreciseDecimal;
285    /// use rust_decimal::Decimal;
286    ///
287    /// let precise = PreciseDecimal::from_parts(
288    ///     Some(Decimal::new(100, 2)),
289    ///     "1.00".to_string()
290    /// );
291    /// assert_eq!(precise.original_string(), "1.00");
292    /// ```
293    pub fn original_string(&self) -> &str {
294        &self.original_string
295    }
296}
297
298/// Converts a `Decimal` to `PreciseDecimal` with derived string representation.
299///
300/// This implementation allows easy conversion from `rust_decimal::Decimal` values
301/// by automatically generating the string representation using the decimal's
302/// `Display` implementation.
303///
304/// # Examples
305///
306/// ```rust
307/// use helios_fhir::PreciseDecimal;
308/// use rust_decimal::Decimal;
309///
310/// let decimal = Decimal::new(12345, 3); // 12.345
311/// let precise: PreciseDecimal = decimal.into();
312/// assert_eq!(precise.value(), Some(decimal));
313/// assert_eq!(precise.original_string(), "12.345");
314/// ```
315impl From<Decimal> for PreciseDecimal {
316    fn from(value: Decimal) -> Self {
317        // Generate string representation from the decimal value
318        let original_string = Arc::from(value.to_string());
319        Self {
320            value: Some(value),
321            original_string,
322        }
323    }
324}
325
326/// Implements serialization for `PreciseDecimal` preserving original format.
327///
328/// This implementation ensures that the exact original string representation
329/// is preserved during JSON serialization, maintaining FHIR's precision
330/// requirements including trailing zeros and specific formatting.
331///
332/// # FHIR Compliance
333///
334/// FHIR requires that decimal values maintain their original precision when
335/// round-tripped through JSON. This implementation uses `serde_json::RawValue`
336/// to serialize the original string directly as a JSON number.
337///
338/// # Examples
339///
340/// ```rust
341/// use helios_fhir::PreciseDecimal;
342/// use rust_decimal::Decimal;
343/// use serde_json;
344///
345/// let precise = PreciseDecimal::from_parts(
346///     Some(Decimal::new(1230, 2)),
347///     "12.30".to_string()
348/// );
349///
350/// let json = serde_json::to_string(&precise).unwrap();
351/// assert_eq!(json, "12.30"); // Preserves trailing zero
352/// ```
353impl Serialize for PreciseDecimal {
354    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
355    where
356        S: Serializer,
357    {
358        // Use RawValue to preserve exact string format in JSON
359        match serde_json::value::RawValue::from_string(self.original_string.to_string()) {
360            Ok(raw_value) => raw_value.serialize(serializer),
361            Err(e) => Err(serde::ser::Error::custom(format!(
362                "Failed to serialize PreciseDecimal '{}': {}",
363                self.original_string, e
364            ))),
365        }
366    }
367}
368
369/// Implements deserialization for `PreciseDecimal` preserving original format.
370///
371/// This implementation deserializes JSON numbers and strings into `PreciseDecimal`
372/// while preserving the exact original string representation. It handles various
373/// JSON formats including scientific notation and nested object structures.
374///
375/// # Supported Formats
376///
377/// - Direct numbers: `12.340`
378/// - String numbers: `"12.340"`
379/// - Scientific notation: `1.234e2` or `1.234E2`
380/// - Nested objects: `{"value": 12.340}` (for macro-generated structures)
381///
382/// # Examples
383///
384/// ```rust
385/// use helios_fhir::PreciseDecimal;
386/// use serde_json;
387///
388/// // Deserialize from JSON number (trailing zeros are normalized)
389/// let precise: PreciseDecimal = serde_json::from_str("12.340").unwrap();
390/// assert_eq!(precise.original_string(), "12.340"); // JSON number format
391///
392/// // Deserialize from JSON string (preserves exact format)
393/// let precise: PreciseDecimal = serde_json::from_str("\"12.340\"").unwrap();
394/// assert_eq!(precise.original_string(), "12.340"); // Preserves string format
395/// ```
396impl<'de> Deserialize<'de> for PreciseDecimal {
397    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
398    where
399        D: Deserializer<'de>,
400    {
401        // Use intermediate Value to capture exact string representation
402        let json_value = serde_json::Value::deserialize(deserializer)?;
403
404        match json_value {
405            serde_json::Value::Number(n) => {
406                // Extract string representation from JSON number
407                let original_string = n.to_string();
408                let parsed_value = Self::parse_decimal_string(&original_string);
409                Ok(PreciseDecimal::from_parts(parsed_value, original_string))
410            }
411            serde_json::Value::String(s) => {
412                // Use string value directly (preserves exact format)
413                let parsed_value = Self::parse_decimal_string(&s);
414                Ok(PreciseDecimal::from_parts(parsed_value, s))
415            }
416            // Handle nested object format (for macro-generated structures)
417            serde_json::Value::Object(map) => match map.get("value") {
418                Some(serde_json::Value::Number(n)) => {
419                    let original_string = n.to_string();
420                    let parsed_value = Self::parse_decimal_string(&original_string);
421                    Ok(PreciseDecimal::from_parts(parsed_value, original_string))
422                }
423                Some(serde_json::Value::String(s)) => {
424                    let original_string = s.clone();
425                    let parsed_value = Self::parse_decimal_string(&original_string);
426                    Ok(PreciseDecimal::from_parts(parsed_value, original_string))
427                }
428                Some(serde_json::Value::Null) => Err(de::Error::invalid_value(
429                    de::Unexpected::Unit,
430                    &"a number or string for decimal value",
431                )),
432                None => Err(de::Error::missing_field("value")),
433                _ => Err(de::Error::invalid_type(
434                    de::Unexpected::Map,
435                    &"a map with a 'value' field containing a number or string",
436                )),
437            },
438            // Handle remaining unexpected types
439            other => Err(de::Error::invalid_type(
440                match other {
441                    serde_json::Value::Null => de::Unexpected::Unit, // Or Unexpected::Option if mapping null to None
442                    serde_json::Value::Bool(b) => de::Unexpected::Bool(b),
443                    serde_json::Value::Array(_) => de::Unexpected::Seq,
444                    _ => de::Unexpected::Other("unexpected JSON type for PreciseDecimal"),
445                },
446                &"a number, string, or object with a 'value' field",
447            )),
448        }
449    }
450}
451
452// --- End PreciseDecimal ---
453
454/// Precision levels for FHIR Date values.
455///
456/// FHIR dates support partial precision, allowing year-only, year-month,
457/// or full date specifications. This enum tracks which components are present.
458#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
459pub enum DatePrecision {
460    /// Year only (YYYY)
461    Year,
462    /// Year and month (YYYY-MM)
463    YearMonth,
464    /// Full date (YYYY-MM-DD)
465    Full,
466}
467
468/// Precision levels for FHIR Time values.
469///
470/// FHIR times support partial precision from hour-only through
471/// sub-second precision. This enum tracks which components are present.
472#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
473pub enum TimePrecision {
474    /// Hour only (HH)
475    Hour,
476    /// Hour and minute (HH:MM)
477    HourMinute,
478    /// Hour, minute, and second (HH:MM:SS)
479    HourMinuteSecond,
480    /// Full time with sub-second precision (HH:MM:SS.sss)
481    Millisecond,
482}
483
484/// Precision levels for FHIR DateTime values.
485///
486/// FHIR datetimes support partial precision from year-only through
487/// sub-second precision with optional timezone information.
488#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
489pub enum DateTimePrecision {
490    /// Year only (YYYY)
491    Year,
492    /// Year and month (YYYY-MM)
493    YearMonth,
494    /// Date only (YYYY-MM-DD)
495    Date,
496    /// Date with hour (YYYY-MM-DDTHH)
497    DateHour,
498    /// Date with hour and minute (YYYY-MM-DDTHH:MM)
499    DateHourMinute,
500    /// Date with time to seconds (YYYY-MM-DDTHH:MM:SS)
501    DateHourMinuteSecond,
502    /// Full datetime with sub-second precision (YYYY-MM-DDTHH:MM:SS.sss)
503    Full,
504}
505
506impl Default for PrecisionDate {
507    fn default() -> Self {
508        // Default to epoch date 1970-01-01
509        Self::from_ymd(1970, 1, 1)
510    }
511}
512
513/// Precision-aware FHIR Date type.
514///
515/// This type preserves the original precision and string representation
516/// of FHIR date values while providing typed access to date components.
517///
518/// # FHIR Date Formats
519/// - `YYYY` - Year only
520/// - `YYYY-MM` - Year and month  
521/// - `YYYY-MM-DD` - Full date
522///
523/// # Examples
524/// ```rust
525/// use helios_fhir::{PrecisionDate, DatePrecision};
526///
527/// // Create a year-only date
528/// let year_date = PrecisionDate::from_year(2023);
529/// assert_eq!(year_date.precision(), DatePrecision::Year);
530/// assert_eq!(year_date.original_string(), "2023");
531///
532/// // Create a full date
533/// let full_date = PrecisionDate::from_ymd(2023, 3, 15);
534/// assert_eq!(full_date.precision(), DatePrecision::Full);
535/// assert_eq!(full_date.original_string(), "2023-03-15");
536/// ```
537#[derive(Debug, Clone, PartialEq, Eq)]
538pub struct PrecisionDate {
539    /// Year component (always present)
540    year: i32,
541    /// Month component (1-12, None for year-only precision)
542    month: Option<u32>,
543    /// Day component (1-31, None for year or year-month precision)
544    day: Option<u32>,
545    /// Precision level of this date
546    precision: DatePrecision,
547    /// Original string representation
548    original_string: Arc<str>,
549}
550
551impl PrecisionDate {
552    /// Creates a year-only precision date.
553    pub fn from_year(year: i32) -> Self {
554        Self {
555            year,
556            month: None,
557            day: None,
558            precision: DatePrecision::Year,
559            original_string: Arc::from(format!("{:04}", year)),
560        }
561    }
562
563    /// Creates a year-month precision date.
564    pub fn from_year_month(year: i32, month: u32) -> Self {
565        Self {
566            year,
567            month: Some(month),
568            day: None,
569            precision: DatePrecision::YearMonth,
570            original_string: Arc::from(format!("{:04}-{:02}", year, month)),
571        }
572    }
573
574    /// Creates a full precision date.
575    pub fn from_ymd(year: i32, month: u32, day: u32) -> Self {
576        Self {
577            year,
578            month: Some(month),
579            day: Some(day),
580            precision: DatePrecision::Full,
581            original_string: Arc::from(format!("{:04}-{:02}-{:02}", year, month, day)),
582        }
583    }
584
585    /// Parses a FHIR date string, preserving precision.
586    pub fn parse(s: &str) -> Option<Self> {
587        // Remove @ prefix if present
588        let s = s.strip_prefix('@').unwrap_or(s);
589
590        let parts: Vec<&str> = s.split('-').collect();
591        match parts.len() {
592            1 => {
593                // Year only
594                let year = parts[0].parse::<i32>().ok()?;
595                Some(Self {
596                    year,
597                    month: None,
598                    day: None,
599                    precision: DatePrecision::Year,
600                    original_string: Arc::from(s),
601                })
602            }
603            2 => {
604                // Year-month
605                let year = parts[0].parse::<i32>().ok()?;
606                let month = parts[1].parse::<u32>().ok()?;
607                if !(1..=12).contains(&month) {
608                    return None;
609                }
610                Some(Self {
611                    year,
612                    month: Some(month),
613                    day: None,
614                    precision: DatePrecision::YearMonth,
615                    original_string: Arc::from(s),
616                })
617            }
618            3 => {
619                // Full date
620                let year = parts[0].parse::<i32>().ok()?;
621                let month = parts[1].parse::<u32>().ok()?;
622                let day = parts[2].parse::<u32>().ok()?;
623                if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
624                    return None;
625                }
626                Some(Self {
627                    year,
628                    month: Some(month),
629                    day: Some(day),
630                    precision: DatePrecision::Full,
631                    original_string: Arc::from(s),
632                })
633            }
634            _ => None,
635        }
636    }
637
638    /// Returns the precision level of this date.
639    pub fn precision(&self) -> DatePrecision {
640        self.precision
641    }
642
643    /// Returns the original string representation.
644    pub fn original_string(&self) -> &str {
645        &self.original_string
646    }
647
648    /// Returns the year component.
649    pub fn year(&self) -> i32 {
650        self.year
651    }
652
653    /// Returns the month component if present.
654    pub fn month(&self) -> Option<u32> {
655        self.month
656    }
657
658    /// Returns the day component if present.
659    pub fn day(&self) -> Option<u32> {
660        self.day
661    }
662
663    /// Converts to a NaiveDate, using defaults for missing components.
664    pub fn to_naive_date(&self) -> NaiveDate {
665        NaiveDate::from_ymd_opt(self.year, self.month.unwrap_or(1), self.day.unwrap_or(1))
666            .expect("Valid date components")
667    }
668
669    /// Compares two dates considering precision.
670    /// Returns None if comparison is indeterminate due to precision differences.
671    pub fn compare(&self, other: &Self) -> Option<Ordering> {
672        // Compare years first
673        match self.year.cmp(&other.year) {
674            Ordering::Equal => {
675                // Years are equal, check month precision
676                match (self.month, other.month) {
677                    (None, None) => Some(Ordering::Equal),
678                    (None, Some(_)) | (Some(_), None) => {
679                        // Different precisions - comparison may be indeterminate
680                        // For < and > we can still determine, but for = it's indeterminate
681                        None
682                    }
683                    (Some(m1), Some(m2)) => match m1.cmp(&m2) {
684                        Ordering::Equal => {
685                            // Months are equal, check day precision
686                            match (self.day, other.day) {
687                                (None, None) => Some(Ordering::Equal),
688                                (None, Some(_)) | (Some(_), None) => {
689                                    // Different precisions - indeterminate
690                                    None
691                                }
692                                (Some(d1), Some(d2)) => Some(d1.cmp(&d2)),
693                            }
694                        }
695                        other => Some(other),
696                    },
697                }
698            }
699            other => Some(other),
700        }
701    }
702}
703
704impl Default for PrecisionTime {
705    fn default() -> Self {
706        // Default to midnight 00:00:00
707        Self::from_hms(0, 0, 0)
708    }
709}
710
711/// Precision-aware FHIR Time type.
712///
713/// This type preserves the original precision and string representation
714/// of FHIR time values. Note that FHIR times do not support timezone information.
715///
716/// # FHIR Time Formats
717/// - `HH` - Hour only
718/// - `HH:MM` - Hour and minute
719/// - `HH:MM:SS` - Hour, minute, and second
720/// - `HH:MM:SS.sss` - Full time with milliseconds
721///
722/// # Examples
723/// ```rust
724/// use helios_fhir::{PrecisionTime, TimePrecision};
725///
726/// // Create an hour-only time
727/// let hour_time = PrecisionTime::from_hour(14);
728/// assert_eq!(hour_time.precision(), TimePrecision::Hour);
729/// assert_eq!(hour_time.original_string(), "14");
730///
731/// // Create a full precision time
732/// let full_time = PrecisionTime::from_hms_milli(14, 30, 45, 123);
733/// assert_eq!(full_time.precision(), TimePrecision::Millisecond);
734/// assert_eq!(full_time.original_string(), "14:30:45.123");
735/// ```
736#[derive(Debug, Clone, PartialEq, Eq)]
737pub struct PrecisionTime {
738    /// Hour component (0-23, always present)
739    hour: u32,
740    /// Minute component (0-59)
741    minute: Option<u32>,
742    /// Second component (0-59)
743    second: Option<u32>,
744    /// Millisecond component (0-999)
745    millisecond: Option<u32>,
746    /// Precision level of this time
747    precision: TimePrecision,
748    /// Original string representation
749    original_string: Arc<str>,
750}
751
752impl PrecisionTime {
753    /// Creates an hour-only precision time.
754    pub fn from_hour(hour: u32) -> Self {
755        Self {
756            hour,
757            minute: None,
758            second: None,
759            millisecond: None,
760            precision: TimePrecision::Hour,
761            original_string: Arc::from(format!("{:02}", hour)),
762        }
763    }
764
765    /// Creates an hour-minute precision time.
766    pub fn from_hm(hour: u32, minute: u32) -> Self {
767        Self {
768            hour,
769            minute: Some(minute),
770            second: None,
771            millisecond: None,
772            precision: TimePrecision::HourMinute,
773            original_string: Arc::from(format!("{:02}:{:02}", hour, minute)),
774        }
775    }
776
777    /// Creates an hour-minute-second precision time.
778    pub fn from_hms(hour: u32, minute: u32, second: u32) -> Self {
779        Self {
780            hour,
781            minute: Some(minute),
782            second: Some(second),
783            millisecond: None,
784            precision: TimePrecision::HourMinuteSecond,
785            original_string: Arc::from(format!("{:02}:{:02}:{:02}", hour, minute, second)),
786        }
787    }
788
789    /// Creates a full precision time with milliseconds.
790    pub fn from_hms_milli(hour: u32, minute: u32, second: u32, millisecond: u32) -> Self {
791        Self {
792            hour,
793            minute: Some(minute),
794            second: Some(second),
795            millisecond: Some(millisecond),
796            precision: TimePrecision::Millisecond,
797            original_string: Arc::from(format!(
798                "{:02}:{:02}:{:02}.{:03}",
799                hour, minute, second, millisecond
800            )),
801        }
802    }
803
804    /// Parses a FHIR time string, preserving precision.
805    pub fn parse(s: &str) -> Option<Self> {
806        // Remove @ and T prefixes if present
807        let s = s.strip_prefix('@').unwrap_or(s);
808        let s = s.strip_prefix('T').unwrap_or(s);
809
810        // Check for timezone (not allowed in FHIR time)
811        if s.contains('+') || s.contains('-') || s.ends_with('Z') {
812            return None;
813        }
814
815        let parts: Vec<&str> = s.split(':').collect();
816        match parts.len() {
817            1 => {
818                // Hour only
819                let hour = parts[0].parse::<u32>().ok()?;
820                if hour > 23 {
821                    return None;
822                }
823                Some(Self {
824                    hour,
825                    minute: None,
826                    second: None,
827                    millisecond: None,
828                    precision: TimePrecision::Hour,
829                    original_string: Arc::from(s),
830                })
831            }
832            2 => {
833                // Hour:minute
834                let hour = parts[0].parse::<u32>().ok()?;
835                let minute = parts[1].parse::<u32>().ok()?;
836                if hour > 23 || minute > 59 {
837                    return None;
838                }
839                Some(Self {
840                    hour,
841                    minute: Some(minute),
842                    second: None,
843                    millisecond: None,
844                    precision: TimePrecision::HourMinute,
845                    original_string: Arc::from(s),
846                })
847            }
848            3 => {
849                // Hour:minute:second[.millisecond]
850                let hour = parts[0].parse::<u32>().ok()?;
851                let minute = parts[1].parse::<u32>().ok()?;
852
853                // Check for milliseconds
854                let (second, millisecond, precision) = if parts[2].contains('.') {
855                    let sec_parts: Vec<&str> = parts[2].split('.').collect();
856                    if sec_parts.len() != 2 {
857                        return None;
858                    }
859                    let second = sec_parts[0].parse::<u32>().ok()?;
860                    // Parse milliseconds, padding or truncating as needed
861                    let ms_str = sec_parts[1];
862                    let ms = if ms_str.len() <= 3 {
863                        // Pad with zeros if needed
864                        let padded = format!("{:0<3}", ms_str);
865                        padded.parse::<u32>().ok()?
866                    } else {
867                        // Truncate to 3 digits
868                        ms_str[..3].parse::<u32>().ok()?
869                    };
870                    (second, Some(ms), TimePrecision::Millisecond)
871                } else {
872                    let second = parts[2].parse::<u32>().ok()?;
873                    (second, None, TimePrecision::HourMinuteSecond)
874                };
875
876                if hour > 23 || minute > 59 || second > 59 {
877                    return None;
878                }
879
880                Some(Self {
881                    hour,
882                    minute: Some(minute),
883                    second: Some(second),
884                    millisecond,
885                    precision,
886                    original_string: Arc::from(s),
887                })
888            }
889            _ => None,
890        }
891    }
892
893    /// Returns the precision level of this time.
894    pub fn precision(&self) -> TimePrecision {
895        self.precision
896    }
897
898    /// Returns the original string representation.
899    pub fn original_string(&self) -> &str {
900        &self.original_string
901    }
902
903    /// Converts to a NaiveTime, using defaults for missing components.
904    pub fn to_naive_time(&self) -> NaiveTime {
905        let milli = self.millisecond.unwrap_or(0);
906        let micro = milli * 1000; // Convert milliseconds to microseconds
907        NaiveTime::from_hms_micro_opt(
908            self.hour,
909            self.minute.unwrap_or(0),
910            self.second.unwrap_or(0),
911            micro,
912        )
913        .expect("Valid time components")
914    }
915
916    /// Compares two times considering precision.
917    /// Per FHIRPath spec: seconds and milliseconds are considered the same precision level
918    pub fn compare(&self, other: &Self) -> Option<Ordering> {
919        match self.hour.cmp(&other.hour) {
920            Ordering::Equal => {
921                match (self.minute, other.minute) {
922                    (None, None) => Some(Ordering::Equal),
923                    (None, Some(_)) | (Some(_), None) => None,
924                    (Some(m1), Some(m2)) => match m1.cmp(&m2) {
925                        Ordering::Equal => {
926                            match (self.second, other.second) {
927                                (None, None) => Some(Ordering::Equal),
928                                (None, Some(_)) | (Some(_), None) => None,
929                                (Some(s1), Some(s2)) => {
930                                    // Per FHIRPath spec: second and millisecond precisions are
931                                    // considered a single precision using decimal comparison
932                                    let ms1 = self.millisecond.unwrap_or(0);
933                                    let ms2 = other.millisecond.unwrap_or(0);
934                                    let total1 = s1 * 1000 + ms1;
935                                    let total2 = s2 * 1000 + ms2;
936                                    Some(total1.cmp(&total2))
937                                }
938                            }
939                        }
940                        other => Some(other),
941                    },
942                }
943            }
944            other => Some(other),
945        }
946    }
947}
948
949impl Default for PrecisionDateTime {
950    fn default() -> Self {
951        // Default to Unix epoch 1970-01-01T00:00:00
952        Self::from_date(1970, 1, 1)
953    }
954}
955
956/// Precision-aware FHIR DateTime type.
957///
958/// This type preserves the original precision and string representation
959/// of FHIR datetime values, including timezone information when present.
960///
961/// # FHIR DateTime Formats
962/// - `YYYY` - Year only
963/// - `YYYY-MM` - Year and month
964/// - `YYYY-MM-DD` - Date only
965/// - `YYYY-MM-DDTHH` - Date with hour
966/// - `YYYY-MM-DDTHH:MM` - Date with hour and minute
967/// - `YYYY-MM-DDTHH:MM:SS` - Date with time to seconds
968/// - `YYYY-MM-DDTHH:MM:SS.sss` - Full datetime with milliseconds
969/// - All time formats can include timezone: `Z`, `+HH:MM`, `-HH:MM`
970///
971/// # Examples
972/// ```rust
973/// use helios_fhir::{PrecisionDateTime, DateTimePrecision};
974///
975/// // Create a date-only datetime
976/// let date_dt = PrecisionDateTime::from_date(2023, 3, 15);
977/// assert_eq!(date_dt.precision(), DateTimePrecision::Date);
978/// assert_eq!(date_dt.original_string(), "2023-03-15");
979///
980/// // Create a full datetime with timezone
981/// let full_dt = PrecisionDateTime::parse("2023-03-15T14:30:45.123Z").unwrap();
982/// assert_eq!(full_dt.precision(), DateTimePrecision::Full);
983/// ```
984#[derive(Debug, Clone, PartialEq, Eq)]
985pub struct PrecisionDateTime {
986    /// Date components
987    pub date: PrecisionDate,
988    /// Time components (if precision includes time)
989    time: Option<PrecisionTime>,
990    /// Timezone offset in minutes from UTC (None means local/unspecified)
991    timezone_offset: Option<i32>,
992    /// Precision level of this datetime
993    precision: DateTimePrecision,
994    /// Original string representation
995    original_string: Arc<str>,
996}
997
998impl PrecisionDateTime {
999    /// Creates a year-only datetime.
1000    pub fn from_year(year: i32) -> Self {
1001        let date = PrecisionDate::from_year(year);
1002        Self {
1003            original_string: date.original_string.clone(),
1004            date,
1005            time: None,
1006            timezone_offset: None,
1007            precision: DateTimePrecision::Year,
1008        }
1009    }
1010
1011    /// Creates a year-month datetime.
1012    pub fn from_year_month(year: i32, month: u32) -> Self {
1013        let date = PrecisionDate::from_year_month(year, month);
1014        Self {
1015            original_string: date.original_string.clone(),
1016            date,
1017            time: None,
1018            timezone_offset: None,
1019            precision: DateTimePrecision::YearMonth,
1020        }
1021    }
1022
1023    /// Creates a date-only datetime.
1024    pub fn from_date(year: i32, month: u32, day: u32) -> Self {
1025        let date = PrecisionDate::from_ymd(year, month, day);
1026        Self {
1027            original_string: date.original_string.clone(),
1028            date,
1029            time: None,
1030            timezone_offset: None,
1031            precision: DateTimePrecision::Date,
1032        }
1033    }
1034
1035    /// Parses a FHIR datetime string, preserving precision and timezone.
1036    pub fn parse(s: &str) -> Option<Self> {
1037        // Remove @ prefix if present
1038        let s = s.strip_prefix('@').unwrap_or(s);
1039
1040        // Check for 'T' separator to determine if time is present
1041        if let Some(t_pos) = s.find('T') {
1042            let date_part = &s[..t_pos];
1043            let time_and_tz = &s[t_pos + 1..];
1044
1045            // Parse date part
1046            let date = PrecisionDate::parse(date_part)?;
1047
1048            // Check for timezone at the end
1049            let (time_part, timezone_offset) = if let Some(stripped) = time_and_tz.strip_suffix('Z')
1050            {
1051                (stripped, Some(0))
1052            } else if let Some(plus_pos) = time_and_tz.rfind('+') {
1053                let tz_str = &time_and_tz[plus_pos + 1..];
1054                let offset = Self::parse_timezone_offset(tz_str)?;
1055                (&time_and_tz[..plus_pos], Some(offset))
1056            } else if let Some(minus_pos) = time_and_tz.rfind('-') {
1057                // Be careful not to confuse negative timezone with date separator
1058                if minus_pos > 0 && time_and_tz[..minus_pos].contains(':') {
1059                    let tz_str = &time_and_tz[minus_pos + 1..];
1060                    let offset = Self::parse_timezone_offset(tz_str)?;
1061                    (&time_and_tz[..minus_pos], Some(-offset))
1062                } else {
1063                    (time_and_tz, None)
1064                }
1065            } else {
1066                (time_and_tz, None)
1067            };
1068
1069            // Parse time part if not empty
1070            let (time, precision) = if time_part.is_empty() {
1071                // Just "T" with no time components (partial datetime)
1072                (
1073                    None,
1074                    match date.precision {
1075                        DatePrecision::Full => DateTimePrecision::Date,
1076                        DatePrecision::YearMonth => DateTimePrecision::YearMonth,
1077                        DatePrecision::Year => DateTimePrecision::Year,
1078                    },
1079                )
1080            } else {
1081                let time = PrecisionTime::parse(time_part)?;
1082                let precision = match time.precision {
1083                    TimePrecision::Hour => DateTimePrecision::DateHour,
1084                    TimePrecision::HourMinute => DateTimePrecision::DateHourMinute,
1085                    TimePrecision::HourMinuteSecond => DateTimePrecision::DateHourMinuteSecond,
1086                    TimePrecision::Millisecond => DateTimePrecision::Full,
1087                };
1088                (Some(time), precision)
1089            };
1090
1091            Some(Self {
1092                date,
1093                time,
1094                timezone_offset,
1095                precision,
1096                original_string: Arc::from(s),
1097            })
1098        } else {
1099            // No 'T' separator, just a date
1100            let date = PrecisionDate::parse(s)?;
1101            let precision = match date.precision {
1102                DatePrecision::Year => DateTimePrecision::Year,
1103                DatePrecision::YearMonth => DateTimePrecision::YearMonth,
1104                DatePrecision::Full => DateTimePrecision::Date,
1105            };
1106
1107            Some(Self {
1108                original_string: Arc::from(s),
1109                date,
1110                time: None,
1111                timezone_offset: None,
1112                precision,
1113            })
1114        }
1115    }
1116
1117    /// Parses a timezone offset string (e.g., "05:30") into minutes.
1118    fn parse_timezone_offset(s: &str) -> Option<i32> {
1119        let parts: Vec<&str> = s.split(':').collect();
1120        match parts.len() {
1121            1 => {
1122                // Just hours
1123                let hours = parts[0].parse::<i32>().ok()?;
1124                Some(hours * 60)
1125            }
1126            2 => {
1127                // Hours and minutes
1128                let hours = parts[0].parse::<i32>().ok()?;
1129                let minutes = parts[1].parse::<i32>().ok()?;
1130                Some(hours * 60 + minutes)
1131            }
1132            _ => None,
1133        }
1134    }
1135
1136    /// Creates a PrecisionDateTime from a PrecisionDate (for date to datetime conversion).
1137    pub fn from_precision_date(date: PrecisionDate) -> Self {
1138        let precision = match date.precision {
1139            DatePrecision::Year => DateTimePrecision::Year,
1140            DatePrecision::YearMonth => DateTimePrecision::YearMonth,
1141            DatePrecision::Full => DateTimePrecision::Date,
1142        };
1143        Self {
1144            original_string: date.original_string.clone(),
1145            date,
1146            time: None,
1147            timezone_offset: None,
1148            precision,
1149        }
1150    }
1151
1152    /// Returns the precision level of this datetime.
1153    pub fn precision(&self) -> DateTimePrecision {
1154        self.precision
1155    }
1156
1157    /// Returns the original string representation.
1158    pub fn original_string(&self) -> &str {
1159        &self.original_string
1160    }
1161
1162    /// Converts to a chrono DateTime<Utc>, using defaults for missing components.
1163    pub fn to_chrono_datetime(&self) -> ChronoDateTime<Utc> {
1164        let naive_date = self.date.to_naive_date();
1165        let naive_time = self
1166            .time
1167            .as_ref()
1168            .map(|t| t.to_naive_time())
1169            .unwrap_or_else(|| NaiveTime::from_hms_opt(0, 0, 0).unwrap());
1170
1171        let naive_dt = naive_date.and_time(naive_time);
1172
1173        // Apply timezone offset if present
1174        if let Some(offset_minutes) = self.timezone_offset {
1175            // The datetime is in local time with the given offset
1176            // We need to subtract the offset to get UTC
1177            let utc_naive = naive_dt - chrono::Duration::minutes(offset_minutes as i64);
1178            ChronoDateTime::<Utc>::from_naive_utc_and_offset(utc_naive, Utc)
1179        } else {
1180            // No timezone means we assume UTC
1181            ChronoDateTime::<Utc>::from_naive_utc_and_offset(naive_dt, Utc)
1182        }
1183    }
1184
1185    /// Compares two datetimes considering precision and timezones.
1186    pub fn compare(&self, other: &Self) -> Option<Ordering> {
1187        // Check if precisions are compatible
1188        // Per FHIRPath spec: seconds and milliseconds are the same precision
1189        let self_precision_normalized = match self.precision {
1190            DateTimePrecision::Full => DateTimePrecision::DateHourMinuteSecond,
1191            p => p,
1192        };
1193        let other_precision_normalized = match other.precision {
1194            DateTimePrecision::Full => DateTimePrecision::DateHourMinuteSecond,
1195            p => p,
1196        };
1197
1198        // If precisions don't match (except for seconds/milliseconds), return None
1199        if self_precision_normalized != other_precision_normalized {
1200            // Special handling for date vs datetime with time components
1201            if self.time.is_none() != other.time.is_none() {
1202                return None;
1203            }
1204        }
1205
1206        // If both have sufficient precision and timezone info, compare as full datetimes
1207        if self.precision >= DateTimePrecision::DateHour
1208            && other.precision >= DateTimePrecision::DateHour
1209            && self.timezone_offset.is_some()
1210            && other.timezone_offset.is_some()
1211        {
1212            // Convert to UTC and compare
1213            return Some(self.to_chrono_datetime().cmp(&other.to_chrono_datetime()));
1214        }
1215
1216        // If one has timezone and the other doesn't, comparison is indeterminate
1217        if self.timezone_offset.is_some() != other.timezone_offset.is_some() {
1218            return None;
1219        }
1220
1221        // Otherwise, compare components with precision awareness
1222        match self.date.compare(&other.date) {
1223            Some(Ordering::Equal) => {
1224                // Dates are equal at their precision level
1225                match (&self.time, &other.time) {
1226                    (None, None) => Some(Ordering::Equal),
1227                    (None, Some(_)) | (Some(_), None) => None, // Different precisions
1228                    (Some(t1), Some(t2)) => t1.compare(t2),
1229                }
1230            }
1231            other => other,
1232        }
1233    }
1234}
1235
1236// === Display Implementations for Precision Types ===
1237
1238impl std::fmt::Display for PrecisionDate {
1239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1240        write!(f, "{}", self.original_string)
1241    }
1242}
1243
1244impl std::fmt::Display for PrecisionDateTime {
1245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1246        write!(f, "{}", self.original_string)
1247    }
1248}
1249
1250impl std::fmt::Display for PrecisionTime {
1251    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1252        write!(f, "{}", self.original_string)
1253    }
1254}
1255
1256// === Serde Implementations for Precision Types ===
1257
1258impl Serialize for PrecisionDate {
1259    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1260    where
1261        S: Serializer,
1262    {
1263        // Serialize as a simple string
1264        serializer.serialize_str(&self.original_string)
1265    }
1266}
1267
1268impl<'de> Deserialize<'de> for PrecisionDate {
1269    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1270    where
1271        D: Deserializer<'de>,
1272    {
1273        let s = String::deserialize(deserializer)?;
1274        PrecisionDate::parse(&s)
1275            .ok_or_else(|| de::Error::custom(format!("Invalid FHIR date format: {}", s)))
1276    }
1277}
1278
1279impl Serialize for PrecisionTime {
1280    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1281    where
1282        S: Serializer,
1283    {
1284        // Serialize as a simple string
1285        serializer.serialize_str(&self.original_string)
1286    }
1287}
1288
1289impl<'de> Deserialize<'de> for PrecisionTime {
1290    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1291    where
1292        D: Deserializer<'de>,
1293    {
1294        let s = String::deserialize(deserializer)?;
1295        PrecisionTime::parse(&s)
1296            .ok_or_else(|| de::Error::custom(format!("Invalid FHIR time format: {}", s)))
1297    }
1298}
1299
1300impl Serialize for PrecisionDateTime {
1301    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1302    where
1303        S: Serializer,
1304    {
1305        // Serialize as a simple string
1306        serializer.serialize_str(&self.original_string)
1307    }
1308}
1309
1310impl<'de> Deserialize<'de> for PrecisionDateTime {
1311    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1312    where
1313        D: Deserializer<'de>,
1314    {
1315        let s = String::deserialize(deserializer)?;
1316        PrecisionDateTime::parse(&s)
1317            .ok_or_else(|| de::Error::custom(format!("Invalid FHIR datetime format: {}", s)))
1318    }
1319}
1320
1321// === PrecisionInstant Implementation ===
1322
1323/// A FHIR instant value that preserves the original string representation and precision.
1324///
1325/// Instants in FHIR must be complete date-time values with timezone information,
1326/// representing a specific moment in time. This type wraps PrecisionDateTime but
1327/// enforces instant-specific constraints.
1328#[derive(Debug, Clone, PartialEq, Eq, Default)]
1329pub struct PrecisionInstant {
1330    inner: PrecisionDateTime,
1331}
1332
1333impl PrecisionInstant {
1334    /// Parses a FHIR instant string.
1335    /// Returns None if the string is not a valid instant (must have full date, time, and timezone).
1336    pub fn parse(s: &str) -> Option<Self> {
1337        // Parse as PrecisionDateTime first
1338        let dt = PrecisionDateTime::parse(s)?;
1339
1340        // For now, accept any valid datetime as an instant
1341        // In strict mode, we could require timezone, but many FHIR resources
1342        // use instant fields without explicit timezones
1343        Some(PrecisionInstant { inner: dt })
1344    }
1345
1346    /// Returns the original string representation
1347    pub fn original_string(&self) -> &str {
1348        self.inner.original_string()
1349    }
1350
1351    /// Get the inner PrecisionDateTime
1352    pub fn as_datetime(&self) -> &PrecisionDateTime {
1353        &self.inner
1354    }
1355
1356    /// Convert to chrono DateTime<Utc>
1357    pub fn to_chrono_datetime(&self) -> ChronoDateTime<Utc> {
1358        // PrecisionDateTime::to_chrono_datetime returns ChronoDateTime<Utc>
1359        self.inner.to_chrono_datetime()
1360    }
1361}
1362
1363impl fmt::Display for PrecisionInstant {
1364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1365        write!(f, "{}", self.inner)
1366    }
1367}
1368
1369impl Serialize for PrecisionInstant {
1370    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1371    where
1372        S: Serializer,
1373    {
1374        self.inner.serialize(serializer)
1375    }
1376}
1377
1378impl<'de> Deserialize<'de> for PrecisionInstant {
1379    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1380    where
1381        D: Deserializer<'de>,
1382    {
1383        let s = String::deserialize(deserializer)?;
1384        PrecisionInstant::parse(&s)
1385            .ok_or_else(|| de::Error::custom(format!("Invalid FHIR instant format: {}", s)))
1386    }
1387}
1388
1389// === IntoEvaluationResult Implementations for Precision Types ===
1390
1391impl IntoEvaluationResult for PrecisionDate {
1392    fn to_evaluation_result(&self) -> EvaluationResult {
1393        EvaluationResult::date(self.original_string.to_string())
1394    }
1395}
1396
1397impl IntoEvaluationResult for PrecisionTime {
1398    fn to_evaluation_result(&self) -> EvaluationResult {
1399        EvaluationResult::time(self.original_string.to_string())
1400    }
1401}
1402
1403impl IntoEvaluationResult for PrecisionDateTime {
1404    fn to_evaluation_result(&self) -> EvaluationResult {
1405        EvaluationResult::datetime(self.original_string.to_string())
1406    }
1407}
1408
1409impl IntoEvaluationResult for PrecisionInstant {
1410    fn to_evaluation_result(&self) -> EvaluationResult {
1411        // Return as datetime with instant type info
1412        EvaluationResult::DateTime(
1413            self.inner.original_string.to_string(),
1414            Some(TypeInfoResult::new("FHIR", "instant")),
1415        )
1416    }
1417}
1418
1419// Removed DecimalElementObjectVisitor
1420
1421#[cfg(feature = "R4")]
1422pub mod r4;
1423#[cfg(feature = "R4B")]
1424pub mod r4b;
1425#[cfg(feature = "R5")]
1426pub mod r5;
1427// #[cfg(feature = "R6")]
1428// pub mod r6; // R6 has issues, keeping commented out for now
1429
1430pub mod parameters;
1431
1432// Re-export commonly used types from parameters module
1433pub use parameters::{ParameterValueAccessor, VersionIndependentParameters};
1434
1435// Removed the FhirSerde trait definition
1436
1437/// Multi-version FHIR resource container supporting version-agnostic operations.
1438///
1439/// This enum provides a unified interface for working with FHIR resources across
1440/// different specification versions. It enables applications to handle multiple
1441/// FHIR versions simultaneously while maintaining type safety and version-specific
1442/// behavior where needed.
1443///
1444/// # Supported Versions
1445///
1446/// - **R4**: FHIR 4.0.1 (normative)
1447/// - **R4B**: FHIR 4.3.0 (ballot)  
1448/// - **R5**: FHIR 5.0.0 (ballot)
1449/// - **R6**: FHIR 6.0.0 (draft)
1450///
1451/// # Feature Flags
1452///
1453/// Each FHIR version is controlled by a corresponding Cargo feature flag.
1454/// Only enabled versions will be available in the enum variants.
1455///
1456/// # Examples
1457///
1458/// ```rust
1459/// use helios_fhir::{FhirResource, FhirVersion};
1460/// # #[cfg(feature = "R4")]
1461/// use helios_fhir::r4::{Patient, HumanName};
1462///
1463/// # #[cfg(feature = "R4")]
1464/// {
1465///     // Create an R4 patient
1466///     let patient = Patient {
1467///         name: Some(vec![HumanName {
1468///             family: Some("Doe".to_string().into()),
1469///             given: Some(vec!["John".to_string().into()]),
1470///             ..Default::default()
1471///         }]),
1472///         ..Default::default()
1473///     };
1474///
1475///     // Wrap in version-agnostic container
1476///     let resource = FhirResource::R4(Box::new(helios_fhir::r4::Resource::Patient(patient)));
1477///     assert_eq!(resource.version(), FhirVersion::R4);
1478/// }
1479/// ```
1480///
1481/// # Version Detection
1482///
1483/// Use the `version()` method to determine which FHIR version a resource uses:
1484///
1485/// ```rust
1486/// # use helios_fhir::{FhirResource, FhirVersion};
1487/// # #[cfg(feature = "R4")]
1488/// # {
1489/// # let resource = FhirResource::R4(Box::new(helios_fhir::r4::Resource::Patient(Default::default())));
1490/// match resource.version() {
1491///     #[cfg(feature = "R4")]
1492///     FhirVersion::R4 => println!("This is an R4 resource"),
1493///     #[cfg(feature = "R4B")]
1494///     FhirVersion::R4B => println!("This is an R4B resource"),
1495///     #[cfg(feature = "R5")]
1496///     FhirVersion::R5 => println!("This is an R5 resource"),
1497///     #[cfg(feature = "R6")]
1498///     FhirVersion::R6 => println!("This is an R6 resource"),
1499/// }
1500/// # }
1501/// ```
1502#[derive(Debug)]
1503pub enum FhirResource {
1504    /// FHIR 4.0.1 (normative) resource
1505    #[cfg(feature = "R4")]
1506    R4(Box<r4::Resource>),
1507    /// FHIR 4.3.0 (ballot) resource
1508    #[cfg(feature = "R4B")]
1509    R4B(Box<r4b::Resource>),
1510    /// FHIR 5.0.0 (ballot) resource
1511    #[cfg(feature = "R5")]
1512    R5(Box<r5::Resource>),
1513    /// FHIR 6.0.0 (draft) resource
1514    #[cfg(feature = "R6")]
1515    R6(Box<r6::Resource>),
1516}
1517
1518impl FhirResource {
1519    /// Returns the FHIR specification version of this resource.
1520    ///
1521    /// This method provides version detection for multi-version applications,
1522    /// enabling version-specific processing logic and compatibility checks.
1523    ///
1524    /// # Returns
1525    ///
1526    /// The `FhirVersion` enum variant corresponding to this resource's specification.
1527    ///
1528    /// # Examples
1529    ///
1530    /// ```rust
1531    /// use helios_fhir::{FhirResource, FhirVersion};
1532    ///
1533    /// # #[cfg(feature = "R5")]
1534    /// # {
1535    /// # let resource = FhirResource::R5(Box::new(helios_fhir::r5::Resource::Patient(Default::default())));
1536    /// let version = resource.version();
1537    /// assert_eq!(version, FhirVersion::R5);
1538    ///
1539    /// // Use version for conditional logic
1540    /// match version {
1541    ///     FhirVersion::R5 => {
1542    ///         println!("Processing R5 resource with latest features");
1543    ///     },
1544    ///     FhirVersion::R4 => {
1545    ///         println!("Processing R4 resource with normative features");
1546    ///     },
1547    ///     _ => {
1548    ///         println!("Processing other FHIR version");
1549    ///     }
1550    /// }
1551    /// # }
1552    /// ```
1553    pub fn version(&self) -> FhirVersion {
1554        match self {
1555            #[cfg(feature = "R4")]
1556            FhirResource::R4(_) => FhirVersion::R4,
1557            #[cfg(feature = "R4B")]
1558            FhirResource::R4B(_) => FhirVersion::R4B,
1559            #[cfg(feature = "R5")]
1560            FhirResource::R5(_) => FhirVersion::R5,
1561            #[cfg(feature = "R6")]
1562            FhirResource::R6(_) => FhirVersion::R6,
1563        }
1564    }
1565}
1566
1567/// Enumeration of supported FHIR specification versions.
1568///
1569/// This enum represents the different versions of the FHIR (Fast Healthcare
1570/// Interoperability Resources) specification that this library supports.
1571/// Each version represents a specific release of the FHIR standard with
1572/// its own set of features, resources, and compatibility requirements.
1573///
1574/// # Version Status
1575///
1576/// - **R4** (4.0.1): Normative version, widely adopted in production
1577/// - **R4B** (4.3.0): Ballot version with additional features
1578/// - **R5** (5.0.0): Ballot version with significant enhancements
1579/// - **R6** (6.0.0): Draft version under active development
1580///
1581/// # Feature Flags
1582///
1583/// Each version is controlled by a corresponding Cargo feature flag:
1584/// - `R4`: Enables FHIR R4 support
1585/// - `R4B`: Enables FHIR R4B support  
1586/// - `R5`: Enables FHIR R5 support
1587/// - `R6`: Enables FHIR R6 support
1588///
1589/// # Examples
1590///
1591/// ```rust
1592/// use helios_fhir::FhirVersion;
1593///
1594/// // Version comparison
1595/// # #[cfg(all(feature = "R4", feature = "R5"))]
1596/// # {
1597/// assert_ne!(FhirVersion::R4, FhirVersion::R5);
1598/// # }
1599///
1600/// // String representation
1601/// # #[cfg(feature = "R4")]
1602/// # {
1603/// let version = FhirVersion::R4;
1604/// assert_eq!(version.as_str(), "R4");
1605/// assert_eq!(version.to_string(), "R4");
1606/// # }
1607/// ```
1608///
1609/// # CLI Integration
1610///
1611/// This enum implements `clap::ValueEnum` for command-line argument parsing:
1612///
1613/// ```rust,no_run
1614/// use clap::Parser;
1615/// use helios_fhir::FhirVersion;
1616///
1617/// #[derive(Parser)]
1618/// struct Args {
1619///     #[arg(value_enum)]
1620///     version: FhirVersion,
1621/// }
1622/// ```
1623#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1624pub enum FhirVersion {
1625    /// FHIR 4.0.1 (normative) - The current normative version
1626    #[cfg(feature = "R4")]
1627    R4,
1628    /// FHIR 4.3.0 (ballot) - Intermediate version with additional features
1629    #[cfg(feature = "R4B")]
1630    R4B,
1631    /// FHIR 5.0.0 (ballot) - Next major version with significant changes
1632    #[cfg(feature = "R5")]
1633    R5,
1634    /// FHIR 6.0.0 (draft) - Future version under development
1635    #[cfg(feature = "R6")]
1636    R6,
1637}
1638
1639impl FhirVersion {
1640    /// Returns the string representation of the FHIR version.
1641    ///
1642    /// This method provides the standard version identifier as used in
1643    /// FHIR documentation, URLs, and configuration files.
1644    ///
1645    /// # Returns
1646    ///
1647    /// A static string slice representing the version (e.g., "R4", "R5").
1648    ///
1649    /// # Examples
1650    ///
1651    /// ```rust
1652    /// use helios_fhir::FhirVersion;
1653    ///
1654    /// # #[cfg(feature = "R4")]
1655    /// assert_eq!(FhirVersion::R4.as_str(), "R4");
1656    /// # #[cfg(feature = "R5")]
1657    /// assert_eq!(FhirVersion::R5.as_str(), "R5");
1658    /// ```
1659    ///
1660    /// # Usage
1661    ///
1662    /// This method is commonly used for:
1663    /// - Logging and debugging output
1664    /// - Configuration file parsing
1665    /// - API endpoint construction
1666    /// - Version-specific resource loading
1667    pub fn as_str(&self) -> &'static str {
1668        match self {
1669            #[cfg(feature = "R4")]
1670            FhirVersion::R4 => "R4",
1671            #[cfg(feature = "R4B")]
1672            FhirVersion::R4B => "R4B",
1673            #[cfg(feature = "R5")]
1674            FhirVersion::R5 => "R5",
1675            #[cfg(feature = "R6")]
1676            FhirVersion::R6 => "R6",
1677        }
1678    }
1679}
1680
1681/// Implements `Display` trait for user-friendly output formatting.
1682///
1683/// This enables `FhirVersion` to be used in string formatting operations
1684/// and provides consistent output across different contexts.
1685///
1686/// # Examples
1687///
1688/// ```rust
1689/// use helios_fhir::FhirVersion;
1690///
1691/// # #[cfg(feature = "R5")]
1692/// # {
1693/// let version = FhirVersion::R5;
1694/// println!("Using FHIR version: {}", version); // Prints: "Using FHIR version: R5"
1695///
1696/// let formatted = format!("fhir-{}.json", version);
1697/// assert_eq!(formatted, "fhir-R5.json");
1698/// # }
1699/// ```
1700impl std::fmt::Display for FhirVersion {
1701    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1702        write!(f, "{}", self.as_str())
1703    }
1704}
1705
1706/// Provides a default FHIR version when R4 feature is enabled.
1707///
1708/// R4 is chosen as the default because it is the current normative version
1709/// of the FHIR specification and is widely adopted in production systems.
1710///
1711/// # Examples
1712///
1713/// ```rust
1714/// use helios_fhir::FhirVersion;
1715///
1716/// # #[cfg(feature = "R4")]
1717/// # {
1718/// let default_version = FhirVersion::default();
1719/// assert_eq!(default_version, FhirVersion::R4);
1720/// # }
1721/// ```
1722#[cfg(feature = "R4")]
1723impl Default for FhirVersion {
1724    fn default() -> Self {
1725        FhirVersion::R4
1726    }
1727}
1728
1729/// Implements `clap::ValueEnum` for command-line argument parsing.
1730///
1731/// This implementation enables `FhirVersion` to be used directly as a command-line
1732/// argument type with clap, providing automatic parsing, validation, and help text
1733/// generation.
1734///
1735/// # Examples
1736///
1737/// ```rust,no_run
1738/// use clap::Parser;
1739/// use helios_fhir::FhirVersion;
1740///
1741/// #[derive(Parser)]
1742/// struct Args {
1743///     /// FHIR specification version to use
1744///     #[arg(value_enum, default_value_t = FhirVersion::default())]
1745///     version: FhirVersion,
1746/// }
1747///
1748/// // Command line: my-app --version R5
1749/// let args = Args::parse();
1750/// println!("Using FHIR version: {}", args.version);
1751/// ```
1752///
1753/// # Generated Help Text
1754///
1755/// When using this enum with clap, the help text will automatically include
1756/// all available FHIR versions based on enabled feature flags.
1757impl clap::ValueEnum for FhirVersion {
1758    fn value_variants<'a>() -> &'a [Self] {
1759        &[
1760            #[cfg(feature = "R4")]
1761            FhirVersion::R4,
1762            #[cfg(feature = "R4B")]
1763            FhirVersion::R4B,
1764            #[cfg(feature = "R5")]
1765            FhirVersion::R5,
1766            #[cfg(feature = "R6")]
1767            FhirVersion::R6,
1768        ]
1769    }
1770
1771    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
1772        Some(clap::builder::PossibleValue::new(self.as_str()))
1773    }
1774}
1775
1776/// Trait for providing FHIR resource type information
1777///
1778/// This trait allows querying which resource types are available in a specific
1779/// FHIR version without hardcoding resource type lists in multiple places.
1780pub trait FhirResourceTypeProvider {
1781    /// Returns a vector of all resource type names supported in this FHIR version
1782    fn get_resource_type_names() -> Vec<&'static str>;
1783
1784    /// Checks if a given type name is a resource type in this FHIR version
1785    fn is_resource_type(type_name: &str) -> bool {
1786        Self::get_resource_type_names()
1787            .iter()
1788            .any(|&resource_type| resource_type.eq_ignore_ascii_case(type_name))
1789    }
1790}
1791
1792/// Trait for providing FHIR complex type information
1793///
1794/// This trait allows querying which complex data types are available in a specific
1795/// FHIR version without hardcoding complex type lists in multiple places.
1796pub trait FhirComplexTypeProvider {
1797    /// Returns a vector of all complex type names supported in this FHIR version
1798    fn get_complex_type_names() -> Vec<&'static str>;
1799
1800    /// Checks if a given type name is a complex type in this FHIR version
1801    fn is_complex_type(type_name: &str) -> bool {
1802        Self::get_complex_type_names()
1803            .iter()
1804            .any(|&complex_type| complex_type.eq_ignore_ascii_case(type_name))
1805    }
1806}
1807
1808// --- Internal Visitor for Element Object Deserialization ---
1809
1810/// Internal visitor struct for deserializing Element objects from JSON maps.
1811///
1812/// This visitor handles the complex deserialization logic for Element<V, E> when
1813/// the JSON input is an object containing id, extension, and value fields.
1814struct ElementObjectVisitor<V, E>(PhantomData<(V, E)>);
1815
1816impl<'de, V, E> Visitor<'de> for ElementObjectVisitor<V, E>
1817where
1818    V: Deserialize<'de>,
1819    E: Deserialize<'de>,
1820{
1821    type Value = Element<V, E>;
1822
1823    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1824        formatter.write_str("an Element object")
1825    }
1826
1827    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1828    where
1829        A: MapAccess<'de>,
1830    {
1831        let mut id: Option<String> = None;
1832        let mut extension: Option<Vec<E>> = None;
1833        let mut value: Option<V> = None;
1834
1835        // Manually deserialize fields from the map
1836        while let Some(key) = map.next_key::<String>()? {
1837            match key.as_str() {
1838                "id" => {
1839                    if id.is_some() {
1840                        return Err(de::Error::duplicate_field("id"));
1841                    }
1842                    id = Some(map.next_value()?);
1843                }
1844                "extension" => {
1845                    if extension.is_some() {
1846                        return Err(de::Error::duplicate_field("extension"));
1847                    }
1848                    extension = Some(map.next_value()?);
1849                }
1850                "value" => {
1851                    if value.is_some() {
1852                        return Err(de::Error::duplicate_field("value"));
1853                    }
1854                    // Deserialize directly into Option<V>
1855                    value = Some(map.next_value()?);
1856                }
1857                // Ignore any unknown fields encountered
1858                _ => {
1859                    let _ = map.next_value::<de::IgnoredAny>()?;
1860                }
1861            }
1862        }
1863
1864        Ok(Element {
1865            id,
1866            extension,
1867            value,
1868        })
1869    }
1870}
1871
1872/// Generic element container supporting FHIR's extension mechanism.
1873///
1874/// In FHIR, most primitive elements can be extended with additional metadata
1875/// through the `id` and `extension` fields. This container type provides
1876/// the infrastructure to support this pattern across all FHIR data types.
1877///
1878/// # Type Parameters
1879///
1880/// * `V` - The value type (e.g., `String`, `i32`, `PreciseDecimal`)
1881/// * `E` - The extension type (typically the generated `Extension` struct)
1882///
1883/// # FHIR Element Structure
1884///
1885/// FHIR elements can appear in three forms:
1886/// 1. **Primitive value**: Just the value itself (e.g., `"text"`, `42`)
1887/// 2. **Extended primitive**: An object with `value`, `id`, and/or `extension` fields
1888/// 3. **Extension-only**: An object with just `id` and/or `extension` (no value)
1889///
1890/// # Examples
1891///
1892/// ```rust
1893/// use helios_fhir::{Element, r4::Extension};
1894///
1895/// // Simple primitive value
1896/// let simple: Element<String, Extension> = Element {
1897///     value: Some("Hello World".to_string()),
1898///     id: None,
1899///     extension: None,
1900/// };
1901///
1902/// // Extended primitive with ID
1903/// let with_id: Element<String, Extension> = Element {
1904///     value: Some("Hello World".to_string()),
1905///     id: Some("text-element-1".to_string()),
1906///     extension: None,
1907/// };
1908///
1909/// // Extension-only element (no value)
1910/// let extension_only: Element<String, Extension> = Element {
1911///     value: None,
1912///     id: Some("disabled-element".to_string()),
1913///     extension: Some(vec![/* extensions */]),
1914/// };
1915/// ```
1916///
1917/// # Serialization Behavior
1918///
1919/// - If only `value` is present: serializes as the primitive value directly
1920/// - If `id` or `extension` are present: serializes as an object with all fields
1921/// - If everything is `None`: serializes as `null`
1922#[derive(Debug, PartialEq, Eq, Clone, Default)]
1923pub struct Element<V, E> {
1924    /// Optional element identifier for referencing within the resource
1925    pub id: Option<String>,
1926    /// Optional extensions providing additional metadata
1927    pub extension: Option<Vec<E>>,
1928    /// The actual primitive value
1929    pub value: Option<V>,
1930}
1931
1932// Custom Deserialize for Element<V, E>
1933// Remove PartialEq/Eq bounds for V and E as they are not needed for deserialization itself
1934impl<'de, V, E> Deserialize<'de> for Element<V, E>
1935where
1936    V: Deserialize<'de> + 'static, // Added 'static for TypeId comparisons
1937    E: Deserialize<'de>,           // Removed PartialEq
1938{
1939    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1940    where
1941        D: Deserializer<'de>,
1942    {
1943        // Use the AnyValueVisitor approach to handle different JSON input types
1944        struct AnyValueVisitor<V, E>(PhantomData<(V, E)>);
1945
1946        impl<'de, V, E> Visitor<'de> for AnyValueVisitor<V, E>
1947        where
1948            V: Deserialize<'de> + 'static,
1949            E: Deserialize<'de>,
1950        {
1951            type Value = Element<V, E>;
1952
1953            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1954                formatter
1955                    .write_str("a primitive value (string, number, boolean), an object, or null")
1956            }
1957
1958            // Handle primitive types by attempting to deserialize V and wrapping it
1959            fn visit_bool<Er>(self, v: bool) -> Result<Self::Value, Er>
1960            where
1961                Er: de::Error,
1962            {
1963                V::deserialize(de::value::BoolDeserializer::new(v)).map(|value| Element {
1964                    id: None,
1965                    extension: None,
1966                    value: Some(value),
1967                })
1968            }
1969            fn visit_i64<Er>(self, v: i64) -> Result<Self::Value, Er>
1970            where
1971                Er: de::Error,
1972            {
1973                V::deserialize(de::value::I64Deserializer::new(v)).map(|value| Element {
1974                    id: None,
1975                    extension: None,
1976                    value: Some(value),
1977                })
1978            }
1979            fn visit_u64<Er>(self, v: u64) -> Result<Self::Value, Er>
1980            where
1981                Er: de::Error,
1982            {
1983                V::deserialize(de::value::U64Deserializer::new(v)).map(|value| Element {
1984                    id: None,
1985                    extension: None,
1986                    value: Some(value),
1987                })
1988            }
1989            fn visit_f64<Er>(self, v: f64) -> Result<Self::Value, Er>
1990            where
1991                Er: de::Error,
1992            {
1993                V::deserialize(de::value::F64Deserializer::new(v)).map(|value| Element {
1994                    id: None,
1995                    extension: None,
1996                    value: Some(value),
1997                })
1998            }
1999            fn visit_str<Er>(self, v: &str) -> Result<Self::Value, Er>
2000            where
2001                Er: de::Error,
2002            {
2003                use std::any::TypeId;
2004
2005                // Try to handle numeric strings for integer types
2006                if TypeId::of::<V>() == TypeId::of::<i64>() {
2007                    if let Ok(int_val) = v.parse::<i64>() {
2008                        return V::deserialize(de::value::I64Deserializer::new(int_val)).map(
2009                            |value| Element {
2010                                id: None,
2011                                extension: None,
2012                                value: Some(value),
2013                            },
2014                        );
2015                    }
2016                } else if TypeId::of::<V>() == TypeId::of::<i32>() {
2017                    if let Ok(int_val) = v.parse::<i32>() {
2018                        return V::deserialize(de::value::I32Deserializer::new(int_val)).map(
2019                            |value| Element {
2020                                id: None,
2021                                extension: None,
2022                                value: Some(value),
2023                            },
2024                        );
2025                    }
2026                } else if TypeId::of::<V>() == TypeId::of::<u64>() {
2027                    if let Ok(int_val) = v.parse::<u64>() {
2028                        return V::deserialize(de::value::U64Deserializer::new(int_val)).map(
2029                            |value| Element {
2030                                id: None,
2031                                extension: None,
2032                                value: Some(value),
2033                            },
2034                        );
2035                    }
2036                } else if TypeId::of::<V>() == TypeId::of::<u32>() {
2037                    if let Ok(int_val) = v.parse::<u32>() {
2038                        return V::deserialize(de::value::U32Deserializer::new(int_val)).map(
2039                            |value| Element {
2040                                id: None,
2041                                extension: None,
2042                                value: Some(value),
2043                            },
2044                        );
2045                    }
2046                }
2047
2048                // Fall back to normal string deserialization
2049                V::deserialize(de::value::StrDeserializer::new(v)).map(|value| Element {
2050                    id: None,
2051                    extension: None,
2052                    value: Some(value),
2053                })
2054            }
2055            fn visit_string<Er>(self, v: String) -> Result<Self::Value, Er>
2056            where
2057                Er: de::Error,
2058            {
2059                use std::any::TypeId;
2060
2061                // Try to handle numeric strings for integer types
2062                if TypeId::of::<V>() == TypeId::of::<i64>() {
2063                    if let Ok(int_val) = v.parse::<i64>() {
2064                        return V::deserialize(de::value::I64Deserializer::new(int_val)).map(
2065                            |value| Element {
2066                                id: None,
2067                                extension: None,
2068                                value: Some(value),
2069                            },
2070                        );
2071                    }
2072                } else if TypeId::of::<V>() == TypeId::of::<i32>() {
2073                    if let Ok(int_val) = v.parse::<i32>() {
2074                        return V::deserialize(de::value::I32Deserializer::new(int_val)).map(
2075                            |value| Element {
2076                                id: None,
2077                                extension: None,
2078                                value: Some(value),
2079                            },
2080                        );
2081                    }
2082                } else if TypeId::of::<V>() == TypeId::of::<u64>() {
2083                    if let Ok(int_val) = v.parse::<u64>() {
2084                        return V::deserialize(de::value::U64Deserializer::new(int_val)).map(
2085                            |value| Element {
2086                                id: None,
2087                                extension: None,
2088                                value: Some(value),
2089                            },
2090                        );
2091                    }
2092                } else if TypeId::of::<V>() == TypeId::of::<u32>() {
2093                    if let Ok(int_val) = v.parse::<u32>() {
2094                        return V::deserialize(de::value::U32Deserializer::new(int_val)).map(
2095                            |value| Element {
2096                                id: None,
2097                                extension: None,
2098                                value: Some(value),
2099                            },
2100                        );
2101                    }
2102                }
2103
2104                // Fall back to normal string deserialization
2105                V::deserialize(de::value::StringDeserializer::new(v.clone())).map(|value| Element {
2106                    // Clone v for error message
2107                    id: None,
2108                    extension: None,
2109                    value: Some(value),
2110                })
2111            }
2112            fn visit_borrowed_str<Er>(self, v: &'de str) -> Result<Self::Value, Er>
2113            where
2114                Er: de::Error,
2115            {
2116                use std::any::TypeId;
2117
2118                // Try to handle numeric strings for integer types
2119                if TypeId::of::<V>() == TypeId::of::<i64>() {
2120                    if let Ok(int_val) = v.parse::<i64>() {
2121                        return V::deserialize(de::value::I64Deserializer::new(int_val)).map(
2122                            |value| Element {
2123                                id: None,
2124                                extension: None,
2125                                value: Some(value),
2126                            },
2127                        );
2128                    }
2129                } else if TypeId::of::<V>() == TypeId::of::<i32>() {
2130                    if let Ok(int_val) = v.parse::<i32>() {
2131                        return V::deserialize(de::value::I32Deserializer::new(int_val)).map(
2132                            |value| Element {
2133                                id: None,
2134                                extension: None,
2135                                value: Some(value),
2136                            },
2137                        );
2138                    }
2139                } else if TypeId::of::<V>() == TypeId::of::<u64>() {
2140                    if let Ok(int_val) = v.parse::<u64>() {
2141                        return V::deserialize(de::value::U64Deserializer::new(int_val)).map(
2142                            |value| Element {
2143                                id: None,
2144                                extension: None,
2145                                value: Some(value),
2146                            },
2147                        );
2148                    }
2149                } else if TypeId::of::<V>() == TypeId::of::<u32>() {
2150                    if let Ok(int_val) = v.parse::<u32>() {
2151                        return V::deserialize(de::value::U32Deserializer::new(int_val)).map(
2152                            |value| Element {
2153                                id: None,
2154                                extension: None,
2155                                value: Some(value),
2156                            },
2157                        );
2158                    }
2159                }
2160
2161                // Fall back to normal string deserialization
2162                V::deserialize(de::value::BorrowedStrDeserializer::new(v)).map(|value| Element {
2163                    id: None,
2164                    extension: None,
2165                    value: Some(value),
2166                })
2167            }
2168            fn visit_bytes<Er>(self, v: &[u8]) -> Result<Self::Value, Er>
2169            where
2170                Er: de::Error,
2171            {
2172                V::deserialize(de::value::BytesDeserializer::new(v)).map(|value| Element {
2173                    id: None,
2174                    extension: None,
2175                    value: Some(value),
2176                })
2177            }
2178            fn visit_byte_buf<Er>(self, v: Vec<u8>) -> Result<Self::Value, Er>
2179            where
2180                Er: de::Error,
2181            {
2182                // Use BytesDeserializer with a slice reference &v
2183                V::deserialize(de::value::BytesDeserializer::new(&v)).map(|value| Element {
2184                    id: None,
2185                    extension: None,
2186                    value: Some(value),
2187                })
2188            }
2189
2190            // Handle null
2191            fn visit_none<Er>(self) -> Result<Self::Value, Er>
2192            where
2193                Er: de::Error,
2194            {
2195                Ok(Element {
2196                    id: None,
2197                    extension: None,
2198                    value: None,
2199                })
2200            }
2201            fn visit_unit<Er>(self) -> Result<Self::Value, Er>
2202            where
2203                Er: de::Error,
2204            {
2205                Ok(Element {
2206                    id: None,
2207                    extension: None,
2208                    value: None,
2209                })
2210            }
2211
2212            // Handle Option<T> by visiting Some
2213            fn visit_some<De>(self, deserializer: De) -> Result<Self::Value, De::Error>
2214            where
2215                De: Deserializer<'de>,
2216            {
2217                // Re-dispatch to deserialize_any to handle the inner type correctly
2218                deserializer.deserialize_any(self)
2219            }
2220
2221            // Handle object
2222            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
2223            where
2224                A: MapAccess<'de>,
2225            {
2226                // Deserialize the map using ElementObjectVisitor
2227                // Need to create a deserializer from the map access
2228                let map_deserializer = de::value::MapAccessDeserializer::new(map);
2229                map_deserializer.deserialize_map(ElementObjectVisitor(PhantomData))
2230            }
2231
2232            // We don't expect sequences for a single Element
2233            fn visit_seq<A>(self, _seq: A) -> Result<Self::Value, A::Error>
2234            where
2235                A: de::SeqAccess<'de>,
2236            {
2237                Err(de::Error::invalid_type(de::Unexpected::Seq, &self))
2238            }
2239        }
2240
2241        // Start deserialization using the visitor
2242        deserializer.deserialize_any(AnyValueVisitor(PhantomData))
2243    }
2244}
2245
2246// Custom Serialize for Element<V, E>
2247// Remove PartialEq/Eq bounds for V and E as they are not needed for serialization itself
2248impl<V, E> Serialize for Element<V, E>
2249where
2250    V: Serialize, // Removed PartialEq + Eq
2251    E: Serialize, // Removed PartialEq
2252{
2253    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2254    where
2255        S: Serializer,
2256    {
2257        // If id and extension are None, serialize value directly (or null)
2258        if self.id.is_none() && self.extension.is_none() {
2259            match &self.value {
2260                Some(val) => val.serialize(serializer),
2261                None => serializer.serialize_none(),
2262            }
2263        } else {
2264            // Otherwise, serialize as an object containing id, extension, value if present
2265            let mut len = 0;
2266            if self.id.is_some() {
2267                len += 1;
2268            }
2269            if self.extension.is_some() {
2270                len += 1;
2271            }
2272            if self.value.is_some() {
2273                len += 1;
2274            }
2275
2276            let mut state = serializer.serialize_struct("Element", len)?;
2277            if let Some(id) = &self.id {
2278                state.serialize_field("id", id)?;
2279            }
2280            if let Some(extension) = &self.extension {
2281                state.serialize_field("extension", extension)?;
2282            }
2283            // Restore value serialization for direct Element serialization
2284            if let Some(value) = &self.value {
2285                state.serialize_field("value", value)?;
2286            }
2287            state.end()
2288        }
2289    }
2290}
2291
2292/// Specialized element container for FHIR decimal values with precision preservation.
2293///
2294/// This type combines the generic `Element` pattern with `PreciseDecimal` to provide
2295/// a complete solution for FHIR decimal elements that require both extension support
2296/// and precision preservation during serialization round-trips.
2297///
2298/// # Type Parameters
2299///
2300/// * `E` - The extension type (typically the generated `Extension` struct)
2301///
2302/// # FHIR Decimal Requirements
2303///
2304/// FHIR decimal elements must:
2305/// - Preserve original string precision (e.g., "12.30" vs "12.3")
2306/// - Support mathematical operations using `Decimal` arithmetic
2307/// - Handle extension metadata through `id` and `extension` fields
2308/// - Serialize back to the exact original format when possible
2309///
2310/// # Examples
2311///
2312/// ```rust
2313/// use helios_fhir::{DecimalElement, PreciseDecimal, r4::Extension};
2314/// use rust_decimal::Decimal;
2315///
2316/// // Create from a Decimal value
2317/// let decimal_elem = DecimalElement::<Extension>::new(Decimal::new(1234, 2)); // 12.34
2318///
2319/// // Create with extensions
2320/// let extended_decimal: DecimalElement<Extension> = DecimalElement {
2321///     value: Some(PreciseDecimal::from_parts(
2322///         Some(Decimal::new(12300, 3)),
2323///         "12.300".to_string()
2324///     )),
2325///     id: Some("precision-example".to_string()),
2326///     extension: Some(vec![/* extensions */]),
2327/// };
2328///
2329/// // Access the mathematical value
2330/// if let Some(precise) = &extended_decimal.value {
2331///     if let Some(decimal_val) = precise.value() {
2332///         println!("Mathematical value: {}", decimal_val);
2333///     }
2334///     println!("Original format: {}", precise.original_string());
2335/// }
2336/// ```
2337///
2338/// # Serialization Behavior
2339///
2340/// - **Value only**: Serializes as a JSON number preserving original precision
2341/// - **With extensions**: Serializes as an object with `value`, `id`, and `extension` fields
2342/// - **No value**: Serializes as an object with just the extension fields, or `null` if empty
2343///
2344/// # Integration with FHIRPath
2345///
2346/// When used with FHIRPath evaluation, `DecimalElement` returns:
2347/// - The `Decimal` value for mathematical operations
2348/// - An object representation when extension metadata is accessed
2349/// - Empty collection when the element has no value or extensions
2350#[derive(Debug, PartialEq, Eq, Clone, Default)]
2351pub struct DecimalElement<E> {
2352    /// Optional element identifier for referencing within the resource
2353    pub id: Option<String>,
2354    /// Optional extensions providing additional metadata
2355    pub extension: Option<Vec<E>>,
2356    /// The decimal value with precision preservation
2357    pub value: Option<PreciseDecimal>,
2358}
2359
2360impl<E> DecimalElement<E> {
2361    /// Creates a new `DecimalElement` with the specified decimal value.
2362    ///
2363    /// This constructor creates a simple decimal element with no extensions or ID,
2364    /// containing only the decimal value. The original string representation is
2365    /// automatically derived from the `Decimal` value's `Display` implementation.
2366    ///
2367    /// # Arguments
2368    ///
2369    /// * `value` - The `Decimal` value to store
2370    ///
2371    /// # Returns
2372    ///
2373    /// A new `DecimalElement` with the value set and `id`/`extension` as `None`.
2374    ///
2375    /// # Examples
2376    ///
2377    /// ```rust
2378    /// use helios_fhir::{DecimalElement, r4::Extension};
2379    /// use rust_decimal::Decimal;
2380    ///
2381    /// // Create a simple decimal element
2382    /// let element = DecimalElement::<Extension>::new(Decimal::new(12345, 3)); // 12.345
2383    ///
2384    /// // Verify the structure
2385    /// assert!(element.id.is_none());
2386    /// assert!(element.extension.is_none());
2387    /// assert!(element.value.is_some());
2388    ///
2389    /// // Access the decimal value
2390    /// if let Some(precise_decimal) = &element.value {
2391    ///     assert_eq!(precise_decimal.value(), Some(Decimal::new(12345, 3)));
2392    ///     assert_eq!(precise_decimal.original_string(), "12.345");
2393    /// }
2394    /// ```
2395    ///
2396    /// # Usage in FHIR Resources
2397    ///
2398    /// This method is typically used when creating FHIR elements programmatically:
2399    ///
2400    /// ```rust
2401    /// use helios_fhir::{DecimalElement, r4::{Extension, Observation}};
2402    /// use rust_decimal::Decimal;
2403    ///
2404    /// let temperature = DecimalElement::<Extension>::new(Decimal::new(3672, 2)); // 36.72
2405    ///
2406    /// // Would be used in an Observation like:
2407    /// // observation.value_quantity.value = Some(temperature);
2408    /// ```
2409    pub fn new(value: Decimal) -> Self {
2410        // Convert the Decimal to PreciseDecimal, which automatically handles
2411        // storing the original string representation via the From trait
2412        let precise_value = PreciseDecimal::from(value);
2413        Self {
2414            id: None,
2415            extension: None,
2416            value: Some(precise_value),
2417        }
2418    }
2419}
2420
2421// Custom Deserialize for DecimalElement<E> using intermediate Value
2422impl<'de, E> Deserialize<'de> for DecimalElement<E>
2423where
2424    E: Deserialize<'de> + Default,
2425{
2426    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2427    where
2428        D: Deserializer<'de>,
2429    {
2430        // Deserialize into an intermediate serde_json::Value first
2431        let json_value = serde_json::Value::deserialize(deserializer)?;
2432
2433        match json_value {
2434            // Handle primitive JSON Number
2435            serde_json::Value::Number(n) => {
2436                // Directly parse the number string to create PreciseDecimal
2437                let s = n.to_string(); // Note: n.to_string() might normalize exponent case (e.g., 'E' -> 'e')
2438                // Replace 'E' with 'e' for parsing
2439                let s_for_parsing = s.replace('E', "e");
2440                // Use from_scientific if 'e' is present, otherwise parse
2441                let parsed_value = if s_for_parsing.contains('e') {
2442                    Decimal::from_scientific(&s_for_parsing).ok()
2443                } else {
2444                    s_for_parsing.parse::<Decimal>().ok()
2445                };
2446                // Store the ORIGINAL string `s` (as returned by n.to_string()).
2447                let pd = PreciseDecimal::from_parts(parsed_value, s);
2448                Ok(DecimalElement {
2449                    id: None,
2450                    extension: None,
2451                    value: Some(pd),
2452                })
2453            }
2454            // Handle primitive JSON String
2455            serde_json::Value::String(s) => {
2456                // Directly parse the string to create PreciseDecimal
2457                // Replace 'E' with 'e' for parsing
2458                let s_for_parsing = s.replace('E', "e");
2459                // Use from_scientific if 'e' is present, otherwise parse
2460                let parsed_value = if s_for_parsing.contains('e') {
2461                    Decimal::from_scientific(&s_for_parsing).ok()
2462                } else {
2463                    s_for_parsing.parse::<Decimal>().ok()
2464                };
2465                // Store the ORIGINAL string `s`.
2466                let pd = PreciseDecimal::from_parts(parsed_value, s); // s is owned, no clone needed
2467                Ok(DecimalElement {
2468                    id: None,
2469                    extension: None,
2470                    value: Some(pd),
2471                })
2472            }
2473            // Handle JSON object: deserialize fields individually
2474            serde_json::Value::Object(map) => {
2475                let mut id: Option<String> = None;
2476                let mut extension: Option<Vec<E>> = None;
2477                let mut value: Option<PreciseDecimal> = None;
2478
2479                for (k, v) in map {
2480                    match k.as_str() {
2481                        "id" => {
2482                            if id.is_some() {
2483                                return Err(de::Error::duplicate_field("id"));
2484                            }
2485                            // Deserialize id directly from its Value
2486                            id = Deserialize::deserialize(v).map_err(de::Error::custom)?;
2487                        }
2488                        "extension" => {
2489                            if extension.is_some() {
2490                                return Err(de::Error::duplicate_field("extension"));
2491                            }
2492                            // Deserialize extension directly from its Value
2493                            extension = Deserialize::deserialize(v).map_err(de::Error::custom)?;
2494                        }
2495                        "value" => {
2496                            if value.is_some() {
2497                                return Err(de::Error::duplicate_field("value"));
2498                            }
2499                            // Deserialize value using PreciseDecimal::deserialize from its Value
2500                            // Handle null explicitly within the value field
2501                            if v.is_null() {
2502                                value = None;
2503                            } else {
2504                                value = Some(
2505                                    PreciseDecimal::deserialize(v).map_err(de::Error::custom)?,
2506                                );
2507                            }
2508                        }
2509                        // Ignore any unknown fields encountered
2510                        _ => {} // Simply ignore unknown fields
2511                    }
2512                }
2513                Ok(DecimalElement {
2514                    id,
2515                    extension,
2516                    value,
2517                })
2518            }
2519            // Handle JSON Null for the whole element
2520            serde_json::Value::Null => Ok(DecimalElement::default()), // Default has value: None
2521            // Handle other unexpected types
2522            other => Err(de::Error::invalid_type(
2523                match other {
2524                    serde_json::Value::Bool(b) => de::Unexpected::Bool(b),
2525                    serde_json::Value::Array(_) => de::Unexpected::Seq,
2526                    _ => de::Unexpected::Other("unexpected JSON type for DecimalElement"),
2527                },
2528                &"a decimal number, string, object, or null",
2529            )),
2530        }
2531    }
2532}
2533
2534// Reinstate custom Serialize implementation for DecimalElement
2535// Remove PartialEq bound for E
2536impl<E> Serialize for DecimalElement<E>
2537where
2538    E: Serialize, // Removed PartialEq bound for E
2539{
2540    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2541    where
2542        S: Serializer,
2543    {
2544        // If we only have a value and no other fields, serialize just the value
2545        if self.id.is_none() && self.extension.is_none() {
2546            if let Some(value) = &self.value {
2547                // Serialize the PreciseDecimal directly, invoking its custom Serialize impl
2548                return value.serialize(serializer);
2549            } else {
2550                // If value is also None, serialize as null
2551                // based on updated test_serialize_decimal_with_no_fields
2552                return serializer.serialize_none();
2553            }
2554        }
2555
2556        // Otherwise, serialize as a struct with all present fields
2557        // Calculate the number of fields that are NOT None
2558        let mut len = 0;
2559        if self.id.is_some() {
2560            len += 1;
2561        }
2562        if self.extension.is_some() {
2563            len += 1;
2564        }
2565        if self.value.is_some() {
2566            len += 1;
2567        }
2568
2569        // Start serializing a struct with the calculated length
2570        let mut state = serializer.serialize_struct("DecimalElement", len)?;
2571
2572        // Serialize 'id' field if it's Some
2573        if let Some(id) = &self.id {
2574            state.serialize_field("id", id)?;
2575        }
2576
2577        // Serialize 'extension' field if it's Some
2578        if let Some(extension) = &self.extension {
2579            state.serialize_field("extension", extension)?;
2580        }
2581
2582        // Serialize 'value' field if it's Some
2583        if let Some(value) = &self.value {
2584            // Serialize the PreciseDecimal directly, invoking its custom Serialize impl
2585            state.serialize_field("value", value)?;
2586        }
2587
2588        // End the struct serialization
2589        state.end()
2590    }
2591}
2592
2593// For Element<V, E> - Returns Object with id, extension, value if present
2594impl<V, E> IntoEvaluationResult for Element<V, E>
2595where
2596    V: IntoEvaluationResult + Clone + 'static,
2597    E: IntoEvaluationResult + Clone,
2598{
2599    fn to_evaluation_result(&self) -> EvaluationResult {
2600        use std::any::TypeId;
2601
2602        // Prioritize returning the primitive value if it exists
2603        if let Some(v) = &self.value {
2604            let result = v.to_evaluation_result();
2605            // For primitive values, we need to preserve FHIR type information
2606            return match result {
2607                EvaluationResult::Boolean(b, _) => {
2608                    // Return FHIR boolean
2609                    EvaluationResult::fhir_boolean(b)
2610                }
2611                EvaluationResult::Integer(i, _) => {
2612                    // Return FHIR integer
2613                    EvaluationResult::fhir_integer(i)
2614                }
2615                #[cfg(not(any(feature = "R4", feature = "R4B")))]
2616                EvaluationResult::Integer64(i, _) => {
2617                    // Return FHIR integer64 (R5 and above)
2618                    EvaluationResult::fhir_integer64(i)
2619                }
2620                EvaluationResult::String(s, _) => {
2621                    // Determine the FHIR type name based on V's type
2622                    let fhir_type_name = if TypeId::of::<V>() == TypeId::of::<String>() {
2623                        // For strings, we need more context to determine the exact FHIR type
2624                        // Default to "string" but this could be date, dateTime, etc.
2625                        "string"
2626                    } else {
2627                        // Default fallback
2628                        "string"
2629                    };
2630                    EvaluationResult::fhir_string(s, fhir_type_name)
2631                }
2632                EvaluationResult::DateTime(dt, type_info) => {
2633                    // Check if V is PrecisionInstant - if so, this is an instant
2634                    if TypeId::of::<V>() == TypeId::of::<PrecisionInstant>() {
2635                        // Return as FHIR instant
2636                        EvaluationResult::DateTime(dt, Some(TypeInfoResult::new("FHIR", "instant")))
2637                    } else {
2638                        // Preserve original type info for PrecisionDateTime
2639                        EvaluationResult::DateTime(dt, type_info)
2640                    }
2641                }
2642                _ => result, // For other types, return as-is
2643            };
2644        } else if self.id.is_some() || self.extension.is_some() {
2645            // If value is None, but id or extension exist, return an Object with those
2646            let mut map = std::collections::HashMap::new();
2647            if let Some(id) = &self.id {
2648                map.insert("id".to_string(), EvaluationResult::string(id.clone()));
2649            }
2650            if let Some(ext) = &self.extension {
2651                let ext_collection: Vec<EvaluationResult> =
2652                    ext.iter().map(|e| e.to_evaluation_result()).collect();
2653                if !ext_collection.is_empty() {
2654                    map.insert(
2655                        "extension".to_string(),
2656                        EvaluationResult::collection(ext_collection),
2657                    );
2658                }
2659            }
2660            // Only return Object if map is not empty (i.e., id or extension was actually present)
2661            if !map.is_empty() {
2662                return EvaluationResult::typed_object(map, "FHIR", "Element");
2663            }
2664        }
2665
2666        // If value, id, and extension are all None, return Empty
2667        EvaluationResult::Empty
2668    }
2669}
2670
2671// For DecimalElement<E> - Returns Decimal value if present, otherwise handles id/extension
2672impl<E> IntoEvaluationResult for DecimalElement<E>
2673where
2674    E: IntoEvaluationResult + Clone,
2675{
2676    fn to_evaluation_result(&self) -> EvaluationResult {
2677        // Prioritize returning the primitive decimal value if it exists
2678        if let Some(precise_decimal) = &self.value {
2679            if let Some(decimal_val) = precise_decimal.value() {
2680                // Return FHIR decimal
2681                return EvaluationResult::fhir_decimal(decimal_val);
2682            }
2683            // If PreciseDecimal holds None for value, fall through to check id/extension
2684        }
2685
2686        // If value is None, but id or extension exist, return an Object with those
2687        if self.id.is_some() || self.extension.is_some() {
2688            let mut map = std::collections::HashMap::new();
2689            if let Some(id) = &self.id {
2690                map.insert("id".to_string(), EvaluationResult::string(id.clone()));
2691            }
2692            if let Some(ext) = &self.extension {
2693                let ext_collection: Vec<EvaluationResult> =
2694                    ext.iter().map(|e| e.to_evaluation_result()).collect();
2695                if !ext_collection.is_empty() {
2696                    map.insert(
2697                        "extension".to_string(),
2698                        EvaluationResult::collection(ext_collection),
2699                    );
2700                }
2701            }
2702            // Only return Object if map is not empty
2703            if !map.is_empty() {
2704                return EvaluationResult::typed_object(map, "FHIR", "decimal");
2705            }
2706        }
2707
2708        // If value, id, and extension are all None, return Empty
2709        EvaluationResult::Empty
2710    }
2711}
2712
2713// Implement the trait for the top-level enum
2714impl IntoEvaluationResult for FhirResource {
2715    fn to_evaluation_result(&self) -> EvaluationResult {
2716        match self {
2717            #[cfg(feature = "R4")]
2718            FhirResource::R4(r) => (*r).to_evaluation_result(), // Call impl on inner Box<r4::Resource>
2719            #[cfg(feature = "R4B")]
2720            FhirResource::R4B(r) => (*r).to_evaluation_result(), // Call impl on inner Box<r4b::Resource>
2721            #[cfg(feature = "R5")]
2722            FhirResource::R5(r) => (*r).to_evaluation_result(), // Call impl on inner Box<r5::Resource>
2723            #[cfg(feature = "R6")]
2724            FhirResource::R6(r) => (*r).to_evaluation_result(), // Call impl on inner Box<r6::Resource>
2725                                                                // Note: If no features are enabled, this match might be empty or non-exhaustive.
2726                                                                // This is generally okay as the enum itself wouldn't be usable.
2727        }
2728    }
2729}
2730
2731#[cfg(test)]
2732mod tests {
2733    use super::*;
2734
2735    #[test]
2736    fn test_integer_string_deserialization() {
2737        // Test deserializing a string "2" into Element<i64, ()>
2738        type TestElement = Element<i64, ()>;
2739
2740        // Test case 1: String containing integer
2741        let json_str = r#""2""#;
2742        let result: Result<TestElement, _> = serde_json::from_str(json_str);
2743        assert!(
2744            result.is_ok(),
2745            "Failed to deserialize string '2' as i64: {:?}",
2746            result.err()
2747        );
2748
2749        let element = result.unwrap();
2750        assert_eq!(element.value, Some(2i64));
2751        assert_eq!(element.id, None);
2752        assert_eq!(element.extension, None);
2753
2754        // Test case 2: Number
2755        let json_num = r#"2"#;
2756        let result: Result<TestElement, _> = serde_json::from_str(json_num);
2757        assert!(
2758            result.is_ok(),
2759            "Failed to deserialize number 2 as i64: {:?}",
2760            result.err()
2761        );
2762
2763        let element = result.unwrap();
2764        assert_eq!(element.value, Some(2i64));
2765    }
2766
2767    #[test]
2768    fn test_i32_string_deserialization() {
2769        type TestElement = Element<i32, ()>;
2770
2771        let json_str = r#""123""#;
2772        let result: Result<TestElement, _> = serde_json::from_str(json_str);
2773        assert!(result.is_ok());
2774
2775        let element = result.unwrap();
2776        assert_eq!(element.value, Some(123i32));
2777    }
2778
2779    #[test]
2780    fn test_invalid_string_fallback() {
2781        type TestElement = Element<i64, ()>;
2782
2783        // Non-numeric string should fail for integer type
2784        let json_str = r#""not_a_number""#;
2785        let result: Result<TestElement, _> = serde_json::from_str(json_str);
2786        assert!(
2787            result.is_err(),
2788            "Should fail to deserialize non-numeric string as i64"
2789        );
2790    }
2791}