helios_sof/
traits.rs

1//! # Version-Agnostic FHIR Abstraction Traits
2//!
3//! This module provides trait abstractions that enable the SOF crate to work
4//! with ViewDefinitions and Bundles across multiple FHIR versions without
5//! duplicating transformation logic. Each FHIR version implements these traits
6//! to provide uniform access to their specific data structures.
7//!
8//! ## Architecture
9//!
10//! The trait system follows a hierarchical pattern:
11//! - Top-level container traits ([`ViewDefinitionTrait`], [`BundleTrait`])
12//! - Component traits ([`ViewDefinitionSelectTrait`], [`ViewDefinitionColumnTrait`], etc.)
13//! - Version-specific implementations for R4, R4B, R5, and R6
14//!
15//! ## Design Benefits
16//!
17//! - **Version Independence**: Core processing logic works with any FHIR version
18//! - **Type Safety**: Compile-time verification of trait implementations
19//! - **Extensibility**: Easy addition of new FHIR versions or features
20//! - **Code Reuse**: Single implementation handles all supported versions
21
22use crate::SofError;
23use helios_fhir::FhirResource;
24use helios_fhirpath::EvaluationResult;
25use helios_fhirpath_support::TypeInfoResult;
26
27/// Trait for abstracting ViewDefinition across FHIR versions.
28///
29/// This trait provides version-agnostic access to ViewDefinition components,
30/// enabling the core processing logic to work uniformly across R4, R4B, R5,
31/// and R6 specifications. Each FHIR version implements this trait to expose
32/// its ViewDefinition structure through a common interface.
33///
34/// # Associated Types
35///
36/// - [`Select`](Self::Select): The select statement type for this FHIR version
37/// - [`Where`](Self::Where): The where clause type for this FHIR version  
38/// - [`Constant`](Self::Constant): The constant definition type for this FHIR version
39///
40/// # Examples
41///
42/// ```rust
43/// use helios_sof::traits::ViewDefinitionTrait;
44///
45/// fn process_any_version<T: ViewDefinitionTrait>(vd: &T) {
46///     if let Some(resource_type) = vd.resource() {
47///         println!("Processing {} resources", resource_type);
48///     }
49///     
50///     if let Some(selects) = vd.select() {
51///         println!("Found {} select statements", selects.len());
52///     }
53/// }
54/// ```
55pub trait ViewDefinitionTrait {
56    /// The select statement type for this FHIR version
57    type Select: ViewDefinitionSelectTrait;
58    /// The where clause type for this FHIR version
59    type Where: ViewDefinitionWhereTrait;
60    /// The constant definition type for this FHIR version
61    type Constant: ViewDefinitionConstantTrait;
62
63    /// Returns the FHIR resource type this ViewDefinition processes
64    fn resource(&self) -> Option<&str>;
65    /// Returns the select statements that define output columns and structure
66    fn select(&self) -> Option<&[Self::Select]>;
67    /// Returns the where clauses that filter resources before processing
68    fn where_clauses(&self) -> Option<&[Self::Where]>;
69    /// Returns the constants/variables available for use in expressions
70    fn constants(&self) -> Option<&[Self::Constant]>;
71}
72
73/// Trait for abstracting ViewDefinitionSelect across FHIR versions.
74///
75/// This trait provides version-agnostic access to select statement components,
76/// including columns, nested selects, iteration constructs, and union operations.
77/// Select statements define the structure and content of the output table.
78///
79/// # Associated Types
80///
81/// - [`Column`](Self::Column): The column definition type for this FHIR version
82/// - [`Select`](Self::Select): Recursive select type for nested structures
83///
84/// # Key Features
85///
86/// - **Column Definitions**: Direct column mappings from FHIRPath to output
87/// - **Nested Selects**: Hierarchical select structures for complex transformations
88/// - **Iteration**: `forEach` and `forEachOrNull` for processing collections
89/// - **Union Operations**: `unionAll` for combining multiple select results
90///
91/// # Examples
92///
93/// ```rust
94/// use helios_sof::traits::ViewDefinitionSelectTrait;
95///
96/// fn analyze_select<T: ViewDefinitionSelectTrait>(select: &T) {
97///     if let Some(columns) = select.column() {
98///         println!("Found {} columns", columns.len());
99///     }
100///     
101///     if let Some(for_each) = select.for_each() {
102///         println!("Iterating over: {}", for_each);
103///     }
104///     
105///     if let Some(union_selects) = select.union_all() {
106///         println!("Union with {} other selects", union_selects.len());
107///     }
108/// }
109/// ```
110pub trait ViewDefinitionSelectTrait {
111    /// The column definition type for this FHIR version
112    type Column: ViewDefinitionColumnTrait;
113    /// Recursive select type for nested structures
114    type Select: ViewDefinitionSelectTrait;
115
116    /// Returns the column definitions for this select statement
117    fn column(&self) -> Option<&[Self::Column]>;
118    /// Returns nested select statements for hierarchical processing
119    fn select(&self) -> Option<&[Self::Select]>;
120    /// Returns the FHIRPath expression for forEach iteration (filters out empty collections)
121    fn for_each(&self) -> Option<&str>;
122    /// Returns the FHIRPath expression for forEachOrNull iteration (includes null rows for empty collections)
123    fn for_each_or_null(&self) -> Option<&str>;
124    /// Returns select statements to union with this one (all results combined)
125    fn union_all(&self) -> Option<&[Self::Select]>;
126}
127
128/// Trait for abstracting ViewDefinitionColumn across FHIR versions.
129///
130/// This trait provides version-agnostic access to column definitions,
131/// which specify how to extract data from FHIR resources and map it
132/// to output table columns. Columns are the fundamental building blocks
133/// of ViewDefinition output structure.
134///
135/// # Key Properties
136///
137/// - **Name**: The output column name in the result table
138/// - **Path**: The FHIRPath expression to extract the value
139/// - **Collection**: Whether this column contains array/collection values
140///
141/// # Examples
142///
143/// ```rust
144/// use helios_sof::traits::ViewDefinitionColumnTrait;
145///
146/// fn describe_column<T: ViewDefinitionColumnTrait>(col: &T) {
147///     if let Some(name) = col.name() {
148///         print!("Column '{}'", name);
149///         
150///         if let Some(path) = col.path() {
151///             print!(" from path '{}'", path);
152///         }
153///         
154///         if col.collection() == Some(true) {
155///             print!(" (collection)");
156///         }
157///         
158///         println!();
159///     }
160/// }
161/// ```
162pub trait ViewDefinitionColumnTrait {
163    /// Returns the name of this column in the output table
164    fn name(&self) -> Option<&str>;
165    /// Returns the FHIRPath expression to extract the column value
166    fn path(&self) -> Option<&str>;
167    /// Returns whether this column should contain collection/array values
168    fn collection(&self) -> Option<bool>;
169}
170
171/// Trait for abstracting ViewDefinitionWhere across FHIR versions.
172///
173/// This trait provides version-agnostic access to where clause definitions,
174/// which filter resources before processing. Where clauses use FHIRPath
175/// expressions that must evaluate to boolean or boolean-coercible values.
176///
177/// # Filtering Logic
178///
179/// Resources are included in processing only if ALL where clauses evaluate to:
180/// - `true` (boolean)
181/// - Non-empty collections
182/// - Any other "truthy" value
183///
184/// Resources are excluded if ANY where clause evaluates to:
185/// - `false` (boolean)
186/// - Empty collections
187/// - Empty/null results
188///
189/// # Examples
190///
191/// ```rust
192/// use helios_sof::traits::ViewDefinitionWhereTrait;
193///
194/// fn check_where_clause<T: ViewDefinitionWhereTrait>(where_clause: &T) {
195///     if let Some(path) = where_clause.path() {
196///         println!("Filter condition: {}", path);
197///         
198///         // Example paths:
199///         // "active = true"                  // Boolean condition
200///         // "name.exists()"                 // Existence check  
201///         // "birthDate >= @1990-01-01"      // Date comparison
202///         // "telecom.where(system='email')" // Collection filtering
203///     }
204/// }
205/// ```
206pub trait ViewDefinitionWhereTrait {
207    /// Returns the FHIRPath expression that must evaluate to true for resource inclusion
208    fn path(&self) -> Option<&str>;
209}
210
211/// Trait for abstracting ViewDefinitionConstant across FHIR versions.
212///
213/// This trait provides version-agnostic access to constant definitions,
214/// which define reusable values that can be referenced in FHIRPath expressions
215/// throughout the ViewDefinition. Constants improve maintainability and
216/// readability of complex transformations.
217///
218/// # Constant Usage
219///
220/// Constants are referenced in FHIRPath expressions using the `%` prefix:
221/// ```fhirpath
222/// // Define constant: name="baseUrl", valueString="http://example.org"
223/// // Use in path: "identifier.where(system = %baseUrl)"
224/// ```
225///
226/// # Supported Types
227///
228/// Constants can hold various FHIR primitive types:
229/// - String values
230/// - Boolean values  
231/// - Integer and decimal numbers
232/// - Date, dateTime, and time values
233/// - Coded values and URIs
234///
235/// # Examples
236///
237/// ```rust
238/// use helios_sof::traits::ViewDefinitionConstantTrait;
239/// use helios_fhirpath::EvaluationResult;
240///
241/// fn process_constant<T: ViewDefinitionConstantTrait>(constant: &T) -> Result<(), Box<dyn std::error::Error>> {
242///     if let Some(name) = constant.name() {
243///         let eval_result = constant.to_evaluation_result()?;
244///         
245///         match eval_result {
246///             EvaluationResult::String(s, _) => {
247///                 println!("String constant '{}' = '{}'", name, s);
248///             },
249///             EvaluationResult::Integer(i, _) => {
250///                 println!("Integer constant '{}' = {}", name, i);
251///             },
252///             EvaluationResult::Boolean(b, _) => {
253///                 println!("Boolean constant '{}' = {}", name, b);
254///             },
255///             _ => {
256///                 println!("Other constant '{}'", name);
257///             }
258///         }
259///     }
260///     Ok(())
261/// }
262/// ```
263pub trait ViewDefinitionConstantTrait {
264    /// Returns the name of this constant for use in FHIRPath expressions (referenced as %name)
265    fn name(&self) -> Option<&str>;
266    /// Converts this constant to an EvaluationResult for use in FHIRPath evaluation
267    fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError>;
268}
269
270/// Trait for abstracting Bundle across FHIR versions.
271///
272/// This trait provides version-agnostic access to Bundle contents,
273/// specifically the collection of resources contained within bundle entries.
274/// Bundles serve as the input data source for ViewDefinition processing.
275///
276/// # Bundle Structure
277///
278/// FHIR Bundles contain:
279/// - Bundle metadata (type, id, etc.)
280/// - Array of bundle entries
281/// - Each entry optionally contains a resource
282///
283/// This trait focuses on extracting the resources for processing,
284/// filtering out entries that don't contain resources.
285///
286/// # Associated Types
287///
288/// - [`Resource`](Self::Resource): The resource type for this FHIR version
289///
290/// # Examples
291///
292/// ```rust
293/// use helios_sof::traits::{BundleTrait, ResourceTrait};
294///
295/// fn analyze_bundle<B: BundleTrait>(bundle: &B)
296/// where
297///     B::Resource: ResourceTrait
298/// {
299///     let resources = bundle.entries();
300///     println!("Bundle contains {} resources", resources.len());
301///     
302///     for resource in resources {
303///         println!("- {} resource", resource.resource_name());
304///     }
305/// }
306/// ```
307pub trait BundleTrait {
308    /// The resource type for this FHIR version
309    type Resource: ResourceTrait;
310
311    /// Returns references to all resources contained in this bundle's entries
312    fn entries(&self) -> Vec<&Self::Resource>;
313}
314
315/// Trait for abstracting Resource across FHIR versions.
316///
317/// This trait provides version-agnostic access to FHIR resource functionality,
318/// enabling the core processing logic to work with resources from any supported
319/// FHIR version. Resources are the primary data objects processed by ViewDefinitions.
320///
321/// # Key Functionality
322///
323/// - **Type Identification**: Determine the resource type (Patient, Observation, etc.)
324/// - **Version Wrapping**: Convert to version-agnostic containers for FHIRPath evaluation
325///
326/// # Examples
327///
328/// ```rust
329/// use helios_sof::traits::ResourceTrait;
330/// use helios_fhir::FhirResource;
331///
332/// fn process_resource<R: ResourceTrait>(resource: &R) {
333///     println!("Processing {} resource", resource.resource_name());
334///     
335///     // Convert to FhirResource for FHIRPath evaluation
336///     let fhir_resource = resource.to_fhir_resource();
337///     
338///     // Now can be used with FHIRPath evaluation context
339///     // let context = EvaluationContext::new(vec![fhir_resource]);
340/// }
341/// ```
342pub trait ResourceTrait: Clone {
343    /// Returns the FHIR resource type name (e.g., "Patient", "Observation")
344    fn resource_name(&self) -> &str;
345    /// Converts this resource to a version-agnostic FhirResource for FHIRPath evaluation
346    fn to_fhir_resource(&self) -> FhirResource;
347    /// Returns the lastUpdated timestamp from the resource's metadata if available
348    fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>>;
349}
350
351// ===== FHIR Version Implementations =====
352//
353// The following modules provide concrete implementations of the abstraction
354// traits for each supported FHIR version. Each implementation maps the
355// version-specific FHIR structures to the common trait interface.
356
357/// R4 (FHIR 4.0.1) trait implementations.
358///
359/// This module implements all abstraction traits for FHIR R4 resources,
360/// providing the mapping between R4-specific ViewDefinition structures
361/// and the version-agnostic trait interfaces.
362#[cfg(feature = "R4")]
363mod r4_impl {
364    use super::*;
365    use helios_fhir::r4::*;
366
367    impl ViewDefinitionTrait for ViewDefinition {
368        type Select = ViewDefinitionSelect;
369        type Where = ViewDefinitionWhere;
370        type Constant = ViewDefinitionConstant;
371
372        fn resource(&self) -> Option<&str> {
373            self.resource.value.as_deref()
374        }
375
376        fn select(&self) -> Option<&[Self::Select]> {
377            self.select.as_deref()
378        }
379
380        fn where_clauses(&self) -> Option<&[Self::Where]> {
381            self.r#where.as_deref()
382        }
383
384        fn constants(&self) -> Option<&[Self::Constant]> {
385            self.constant.as_deref()
386        }
387    }
388
389    impl ViewDefinitionSelectTrait for ViewDefinitionSelect {
390        type Column = ViewDefinitionSelectColumn;
391        type Select = ViewDefinitionSelect;
392
393        fn column(&self) -> Option<&[Self::Column]> {
394            self.column.as_deref()
395        }
396
397        fn select(&self) -> Option<&[Self::Select]> {
398            self.select.as_deref()
399        }
400
401        fn for_each(&self) -> Option<&str> {
402            self.for_each.as_ref()?.value.as_deref()
403        }
404
405        fn for_each_or_null(&self) -> Option<&str> {
406            self.for_each_or_null.as_ref()?.value.as_deref()
407        }
408
409        fn union_all(&self) -> Option<&[Self::Select]> {
410            self.union_all.as_deref()
411        }
412    }
413
414    impl ViewDefinitionColumnTrait for ViewDefinitionSelectColumn {
415        fn name(&self) -> Option<&str> {
416            self.name.value.as_deref()
417        }
418
419        fn path(&self) -> Option<&str> {
420            self.path.value.as_deref()
421        }
422
423        fn collection(&self) -> Option<bool> {
424            self.collection.as_ref()?.value
425        }
426    }
427
428    impl ViewDefinitionWhereTrait for ViewDefinitionWhere {
429        fn path(&self) -> Option<&str> {
430            self.path.value.as_deref()
431        }
432    }
433
434    impl ViewDefinitionConstantTrait for ViewDefinitionConstant {
435        fn name(&self) -> Option<&str> {
436            self.name.value.as_deref()
437        }
438
439        fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError> {
440            let name = self.name().unwrap_or("unknown");
441
442            if let Some(value) = &self.value {
443                let eval_result = match value {
444                    ViewDefinitionConstantValue::String(s) => {
445                        EvaluationResult::String(s.value.clone().unwrap_or_default(), None)
446                    }
447                    ViewDefinitionConstantValue::Boolean(b) => {
448                        EvaluationResult::Boolean(b.value.unwrap_or(false), None)
449                    }
450                    ViewDefinitionConstantValue::Integer(i) => {
451                        EvaluationResult::Integer(i.value.unwrap_or(0) as i64, None)
452                    }
453                    ViewDefinitionConstantValue::Decimal(d) => {
454                        if let Some(precise_decimal) = &d.value {
455                            match precise_decimal.original_string().parse() {
456                                Ok(decimal_value) => EvaluationResult::Decimal(decimal_value, None),
457                                Err(_) => {
458                                    return Err(SofError::InvalidViewDefinition(format!(
459                                        "Invalid decimal value for constant '{}'",
460                                        name
461                                    )));
462                                }
463                            }
464                        } else {
465                            EvaluationResult::Decimal("0".parse().unwrap(), None)
466                        }
467                    }
468                    ViewDefinitionConstantValue::Date(d) => {
469                        EvaluationResult::Date(d.value.clone().unwrap_or_default(), None)
470                    }
471                    ViewDefinitionConstantValue::DateTime(dt) => {
472                        let value = dt.value.clone().unwrap_or_default();
473                        // Ensure DateTime values have the "@" prefix for FHIRPath
474                        let prefixed = if value.starts_with("@") {
475                            value
476                        } else {
477                            format!("@{}", value)
478                        };
479                        EvaluationResult::DateTime(
480                            prefixed,
481                            Some(TypeInfoResult::new("FHIR", "dateTime")),
482                        )
483                    }
484                    ViewDefinitionConstantValue::Time(t) => {
485                        let value = t.value.clone().unwrap_or_default();
486                        // Ensure Time values have the "@T" prefix for FHIRPath
487                        let prefixed = if value.starts_with("@T") {
488                            value
489                        } else {
490                            format!("@T{}", value)
491                        };
492                        EvaluationResult::Time(prefixed, None)
493                    }
494                    ViewDefinitionConstantValue::Code(c) => {
495                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
496                    }
497                    ViewDefinitionConstantValue::Base64Binary(b) => {
498                        EvaluationResult::String(b.value.clone().unwrap_or_default(), None)
499                    }
500                    ViewDefinitionConstantValue::Id(i) => {
501                        EvaluationResult::String(i.value.clone().unwrap_or_default(), None)
502                    }
503                    ViewDefinitionConstantValue::Instant(i) => {
504                        let value = i.value.clone().unwrap_or_default();
505                        // Ensure Instant values have the "@" prefix for FHIRPath
506                        let prefixed = if value.starts_with("@") {
507                            value
508                        } else {
509                            format!("@{}", value)
510                        };
511                        EvaluationResult::DateTime(
512                            prefixed,
513                            Some(TypeInfoResult::new("FHIR", "instant")),
514                        )
515                    }
516                    ViewDefinitionConstantValue::Oid(o) => {
517                        EvaluationResult::String(o.value.clone().unwrap_or_default(), None)
518                    }
519                    ViewDefinitionConstantValue::PositiveInt(p) => {
520                        EvaluationResult::Integer(p.value.unwrap_or(1) as i64, None)
521                    }
522                    ViewDefinitionConstantValue::UnsignedInt(u) => {
523                        EvaluationResult::Integer(u.value.unwrap_or(0) as i64, None)
524                    }
525                    ViewDefinitionConstantValue::Uri(u) => {
526                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
527                    }
528                    ViewDefinitionConstantValue::Url(u) => {
529                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
530                    }
531                    ViewDefinitionConstantValue::Uuid(u) => {
532                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
533                    }
534                    ViewDefinitionConstantValue::Canonical(c) => {
535                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
536                    }
537                };
538
539                Ok(eval_result)
540            } else {
541                Err(SofError::InvalidViewDefinition(format!(
542                    "Constant '{}' must have a value",
543                    name
544                )))
545            }
546        }
547    }
548
549    impl BundleTrait for Bundle {
550        type Resource = Resource;
551
552        fn entries(&self) -> Vec<&Self::Resource> {
553            self.entry
554                .as_ref()
555                .map(|entries| entries.iter().filter_map(|e| e.resource.as_ref()).collect())
556                .unwrap_or_default()
557        }
558    }
559
560    impl ResourceTrait for Resource {
561        fn resource_name(&self) -> &str {
562            self.resource_name()
563        }
564
565        fn to_fhir_resource(&self) -> FhirResource {
566            FhirResource::R4(Box::new(self.clone()))
567        }
568
569        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
570            self.get_last_updated()
571        }
572    }
573}
574
575/// R4B (FHIR 4.3.0) trait implementations.
576///
577/// This module implements all abstraction traits for FHIR R4B resources,
578/// providing the mapping between R4B-specific ViewDefinition structures
579/// and the version-agnostic trait interfaces.
580#[cfg(feature = "R4B")]
581mod r4b_impl {
582    use super::*;
583    use helios_fhir::r4b::*;
584
585    impl ViewDefinitionTrait for ViewDefinition {
586        type Select = ViewDefinitionSelect;
587        type Where = ViewDefinitionWhere;
588        type Constant = ViewDefinitionConstant;
589
590        fn resource(&self) -> Option<&str> {
591            self.resource.value.as_deref()
592        }
593
594        fn select(&self) -> Option<&[Self::Select]> {
595            self.select.as_deref()
596        }
597
598        fn where_clauses(&self) -> Option<&[Self::Where]> {
599            self.r#where.as_deref()
600        }
601
602        fn constants(&self) -> Option<&[Self::Constant]> {
603            self.constant.as_deref()
604        }
605    }
606
607    impl ViewDefinitionSelectTrait for ViewDefinitionSelect {
608        type Column = ViewDefinitionSelectColumn;
609        type Select = ViewDefinitionSelect;
610
611        fn column(&self) -> Option<&[Self::Column]> {
612            self.column.as_deref()
613        }
614
615        fn select(&self) -> Option<&[Self::Select]> {
616            self.select.as_deref()
617        }
618
619        fn for_each(&self) -> Option<&str> {
620            self.for_each.as_ref()?.value.as_deref()
621        }
622
623        fn for_each_or_null(&self) -> Option<&str> {
624            self.for_each_or_null.as_ref()?.value.as_deref()
625        }
626
627        fn union_all(&self) -> Option<&[Self::Select]> {
628            self.union_all.as_deref()
629        }
630    }
631
632    impl ViewDefinitionColumnTrait for ViewDefinitionSelectColumn {
633        fn name(&self) -> Option<&str> {
634            self.name.value.as_deref()
635        }
636
637        fn path(&self) -> Option<&str> {
638            self.path.value.as_deref()
639        }
640
641        fn collection(&self) -> Option<bool> {
642            self.collection.as_ref()?.value
643        }
644    }
645
646    impl ViewDefinitionWhereTrait for ViewDefinitionWhere {
647        fn path(&self) -> Option<&str> {
648            self.path.value.as_deref()
649        }
650    }
651
652    impl ViewDefinitionConstantTrait for ViewDefinitionConstant {
653        fn name(&self) -> Option<&str> {
654            self.name.value.as_deref()
655        }
656
657        fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError> {
658            let name = self.name().unwrap_or("unknown");
659
660            if let Some(value) = &self.value {
661                let eval_result = match value {
662                    ViewDefinitionConstantValue::String(s) => {
663                        EvaluationResult::String(s.value.clone().unwrap_or_default(), None)
664                    }
665                    ViewDefinitionConstantValue::Boolean(b) => {
666                        EvaluationResult::Boolean(b.value.unwrap_or(false), None)
667                    }
668                    ViewDefinitionConstantValue::Integer(i) => {
669                        EvaluationResult::Integer(i.value.unwrap_or(0) as i64, None)
670                    }
671                    ViewDefinitionConstantValue::Decimal(d) => {
672                        if let Some(precise_decimal) = &d.value {
673                            match precise_decimal.original_string().parse() {
674                                Ok(decimal_value) => EvaluationResult::Decimal(decimal_value, None),
675                                Err(_) => {
676                                    return Err(SofError::InvalidViewDefinition(format!(
677                                        "Invalid decimal value for constant '{}'",
678                                        name
679                                    )));
680                                }
681                            }
682                        } else {
683                            EvaluationResult::Decimal("0".parse().unwrap(), None)
684                        }
685                    }
686                    ViewDefinitionConstantValue::Date(d) => {
687                        EvaluationResult::Date(d.value.clone().unwrap_or_default(), None)
688                    }
689                    ViewDefinitionConstantValue::DateTime(dt) => {
690                        let value = dt.value.clone().unwrap_or_default();
691                        // Ensure DateTime values have the "@" prefix for FHIRPath
692                        let prefixed = if value.starts_with("@") {
693                            value
694                        } else {
695                            format!("@{}", value)
696                        };
697                        EvaluationResult::DateTime(
698                            prefixed,
699                            Some(TypeInfoResult::new("FHIR", "dateTime")),
700                        )
701                    }
702                    ViewDefinitionConstantValue::Time(t) => {
703                        let value = t.value.clone().unwrap_or_default();
704                        // Ensure Time values have the "@T" prefix for FHIRPath
705                        let prefixed = if value.starts_with("@T") {
706                            value
707                        } else {
708                            format!("@T{}", value)
709                        };
710                        EvaluationResult::Time(prefixed, None)
711                    }
712                    ViewDefinitionConstantValue::Code(c) => {
713                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
714                    }
715                    ViewDefinitionConstantValue::Base64Binary(b) => {
716                        EvaluationResult::String(b.value.clone().unwrap_or_default(), None)
717                    }
718                    ViewDefinitionConstantValue::Id(i) => {
719                        EvaluationResult::String(i.value.clone().unwrap_or_default(), None)
720                    }
721                    ViewDefinitionConstantValue::Instant(i) => {
722                        let value = i.value.clone().unwrap_or_default();
723                        // Ensure Instant values have the "@" prefix for FHIRPath
724                        let prefixed = if value.starts_with("@") {
725                            value
726                        } else {
727                            format!("@{}", value)
728                        };
729                        EvaluationResult::DateTime(
730                            prefixed,
731                            Some(TypeInfoResult::new("FHIR", "instant")),
732                        )
733                    }
734                    ViewDefinitionConstantValue::Oid(o) => {
735                        EvaluationResult::String(o.value.clone().unwrap_or_default(), None)
736                    }
737                    ViewDefinitionConstantValue::PositiveInt(p) => {
738                        EvaluationResult::Integer(p.value.unwrap_or(1) as i64, None)
739                    }
740                    ViewDefinitionConstantValue::UnsignedInt(u) => {
741                        EvaluationResult::Integer(u.value.unwrap_or(0) as i64, None)
742                    }
743                    ViewDefinitionConstantValue::Uri(u) => {
744                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
745                    }
746                    ViewDefinitionConstantValue::Url(u) => {
747                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
748                    }
749                    ViewDefinitionConstantValue::Uuid(u) => {
750                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
751                    }
752                    ViewDefinitionConstantValue::Canonical(c) => {
753                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
754                    }
755                };
756
757                Ok(eval_result)
758            } else {
759                Err(SofError::InvalidViewDefinition(format!(
760                    "Constant '{}' must have a value",
761                    name
762                )))
763            }
764        }
765    }
766
767    impl BundleTrait for Bundle {
768        type Resource = Resource;
769
770        fn entries(&self) -> Vec<&Self::Resource> {
771            self.entry
772                .as_ref()
773                .map(|entries| entries.iter().filter_map(|e| e.resource.as_ref()).collect())
774                .unwrap_or_default()
775        }
776    }
777
778    impl ResourceTrait for Resource {
779        fn resource_name(&self) -> &str {
780            self.resource_name()
781        }
782
783        fn to_fhir_resource(&self) -> FhirResource {
784            FhirResource::R4B(Box::new(self.clone()))
785        }
786
787        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
788            self.get_last_updated()
789        }
790    }
791}
792
793/// R5 (FHIR 5.0.0) trait implementations.
794///
795/// This module implements all abstraction traits for FHIR R5 resources,
796/// providing the mapping between R5-specific ViewDefinition structures
797/// and the version-agnostic trait interfaces. R5 introduces the Integer64
798/// data type for constant values.
799#[cfg(feature = "R5")]
800mod r5_impl {
801    use super::*;
802    use helios_fhir::r5::*;
803
804    impl ViewDefinitionTrait for ViewDefinition {
805        type Select = ViewDefinitionSelect;
806        type Where = ViewDefinitionWhere;
807        type Constant = ViewDefinitionConstant;
808
809        fn resource(&self) -> Option<&str> {
810            self.resource.value.as_deref()
811        }
812
813        fn select(&self) -> Option<&[Self::Select]> {
814            self.select.as_deref()
815        }
816
817        fn where_clauses(&self) -> Option<&[Self::Where]> {
818            self.r#where.as_deref()
819        }
820
821        fn constants(&self) -> Option<&[Self::Constant]> {
822            self.constant.as_deref()
823        }
824    }
825
826    impl ViewDefinitionSelectTrait for ViewDefinitionSelect {
827        type Column = ViewDefinitionSelectColumn;
828        type Select = ViewDefinitionSelect;
829
830        fn column(&self) -> Option<&[Self::Column]> {
831            self.column.as_deref()
832        }
833
834        fn select(&self) -> Option<&[Self::Select]> {
835            self.select.as_deref()
836        }
837
838        fn for_each(&self) -> Option<&str> {
839            self.for_each.as_ref()?.value.as_deref()
840        }
841
842        fn for_each_or_null(&self) -> Option<&str> {
843            self.for_each_or_null.as_ref()?.value.as_deref()
844        }
845
846        fn union_all(&self) -> Option<&[Self::Select]> {
847            self.union_all.as_deref()
848        }
849    }
850
851    impl ViewDefinitionColumnTrait for ViewDefinitionSelectColumn {
852        fn name(&self) -> Option<&str> {
853            self.name.value.as_deref()
854        }
855
856        fn path(&self) -> Option<&str> {
857            self.path.value.as_deref()
858        }
859
860        fn collection(&self) -> Option<bool> {
861            self.collection.as_ref()?.value
862        }
863    }
864
865    impl ViewDefinitionWhereTrait for ViewDefinitionWhere {
866        fn path(&self) -> Option<&str> {
867            self.path.value.as_deref()
868        }
869    }
870
871    impl ViewDefinitionConstantTrait for ViewDefinitionConstant {
872        fn name(&self) -> Option<&str> {
873            self.name.value.as_deref()
874        }
875
876        fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError> {
877            let name = self.name().unwrap_or("unknown");
878
879            if let Some(value) = &self.value {
880                // R5 implementation identical to R4
881                let eval_result = match value {
882                    ViewDefinitionConstantValue::String(s) => {
883                        EvaluationResult::String(s.value.clone().unwrap_or_default(), None)
884                    }
885                    ViewDefinitionConstantValue::Boolean(b) => {
886                        EvaluationResult::Boolean(b.value.unwrap_or(false), None)
887                    }
888                    ViewDefinitionConstantValue::Integer(i) => {
889                        EvaluationResult::Integer(i.value.unwrap_or(0) as i64, None)
890                    }
891                    ViewDefinitionConstantValue::Decimal(d) => {
892                        if let Some(precise_decimal) = &d.value {
893                            match precise_decimal.original_string().parse() {
894                                Ok(decimal_value) => EvaluationResult::Decimal(decimal_value, None),
895                                Err(_) => {
896                                    return Err(SofError::InvalidViewDefinition(format!(
897                                        "Invalid decimal value for constant '{}'",
898                                        name
899                                    )));
900                                }
901                            }
902                        } else {
903                            EvaluationResult::Decimal("0".parse().unwrap(), None)
904                        }
905                    }
906                    ViewDefinitionConstantValue::Date(d) => {
907                        EvaluationResult::Date(d.value.clone().unwrap_or_default(), None)
908                    }
909                    ViewDefinitionConstantValue::DateTime(dt) => {
910                        let value = dt.value.clone().unwrap_or_default();
911                        // Ensure DateTime values have the "@" prefix for FHIRPath
912                        let prefixed = if value.starts_with("@") {
913                            value
914                        } else {
915                            format!("@{}", value)
916                        };
917                        EvaluationResult::DateTime(
918                            prefixed,
919                            Some(TypeInfoResult::new("FHIR", "dateTime")),
920                        )
921                    }
922                    ViewDefinitionConstantValue::Time(t) => {
923                        let value = t.value.clone().unwrap_or_default();
924                        // Ensure Time values have the "@T" prefix for FHIRPath
925                        let prefixed = if value.starts_with("@T") {
926                            value
927                        } else {
928                            format!("@T{}", value)
929                        };
930                        EvaluationResult::Time(prefixed, None)
931                    }
932                    ViewDefinitionConstantValue::Code(c) => {
933                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
934                    }
935                    ViewDefinitionConstantValue::Base64Binary(b) => {
936                        EvaluationResult::String(b.value.clone().unwrap_or_default(), None)
937                    }
938                    ViewDefinitionConstantValue::Id(i) => {
939                        EvaluationResult::String(i.value.clone().unwrap_or_default(), None)
940                    }
941                    ViewDefinitionConstantValue::Instant(i) => {
942                        let value = i.value.clone().unwrap_or_default();
943                        // Ensure Instant values have the "@" prefix for FHIRPath
944                        let prefixed = if value.starts_with("@") {
945                            value
946                        } else {
947                            format!("@{}", value)
948                        };
949                        EvaluationResult::DateTime(
950                            prefixed,
951                            Some(TypeInfoResult::new("FHIR", "instant")),
952                        )
953                    }
954                    ViewDefinitionConstantValue::Oid(o) => {
955                        EvaluationResult::String(o.value.clone().unwrap_or_default(), None)
956                    }
957                    ViewDefinitionConstantValue::PositiveInt(p) => {
958                        EvaluationResult::Integer(p.value.unwrap_or(1) as i64, None)
959                    }
960                    ViewDefinitionConstantValue::UnsignedInt(u) => {
961                        EvaluationResult::Integer(u.value.unwrap_or(0) as i64, None)
962                    }
963                    ViewDefinitionConstantValue::Uri(u) => {
964                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
965                    }
966                    ViewDefinitionConstantValue::Url(u) => {
967                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
968                    }
969                    ViewDefinitionConstantValue::Uuid(u) => {
970                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
971                    }
972                    ViewDefinitionConstantValue::Canonical(c) => {
973                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
974                    }
975                    ViewDefinitionConstantValue::Integer64(i) => {
976                        EvaluationResult::Integer64(i.value.unwrap_or(0), None)
977                    }
978                };
979
980                Ok(eval_result)
981            } else {
982                Err(SofError::InvalidViewDefinition(format!(
983                    "Constant '{}' must have a value",
984                    name
985                )))
986            }
987        }
988    }
989
990    impl BundleTrait for Bundle {
991        type Resource = Resource;
992
993        fn entries(&self) -> Vec<&Self::Resource> {
994            self.entry
995                .as_ref()
996                .map(|entries| {
997                    entries
998                        .iter()
999                        .filter_map(|e| e.resource.as_deref()) // Note: R5 uses Box<Resource>
1000                        .collect()
1001                })
1002                .unwrap_or_default()
1003        }
1004    }
1005
1006    impl ResourceTrait for Resource {
1007        fn resource_name(&self) -> &str {
1008            self.resource_name()
1009        }
1010
1011        fn to_fhir_resource(&self) -> FhirResource {
1012            FhirResource::R5(Box::new(self.clone()))
1013        }
1014
1015        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
1016            self.get_last_updated()
1017        }
1018    }
1019}
1020
1021/// R6 (FHIR 6.0.0) trait implementations.
1022///
1023/// This module implements all abstraction traits for FHIR R6 resources,
1024/// providing the mapping between R6-specific ViewDefinition structures
1025/// and the version-agnostic trait interfaces. R6 continues to support
1026/// the Integer64 data type introduced in R5.
1027#[cfg(feature = "R6")]
1028mod r6_impl {
1029    use super::*;
1030    use helios_fhir::r6::*;
1031
1032    impl ViewDefinitionTrait for ViewDefinition {
1033        type Select = ViewDefinitionSelect;
1034        type Where = ViewDefinitionWhere;
1035        type Constant = ViewDefinitionConstant;
1036
1037        fn resource(&self) -> Option<&str> {
1038            self.resource.value.as_deref()
1039        }
1040
1041        fn select(&self) -> Option<&[Self::Select]> {
1042            self.select.as_deref()
1043        }
1044
1045        fn where_clauses(&self) -> Option<&[Self::Where]> {
1046            self.r#where.as_deref()
1047        }
1048
1049        fn constants(&self) -> Option<&[Self::Constant]> {
1050            self.constant.as_deref()
1051        }
1052    }
1053
1054    impl ViewDefinitionSelectTrait for ViewDefinitionSelect {
1055        type Column = ViewDefinitionSelectColumn;
1056        type Select = ViewDefinitionSelect;
1057
1058        fn column(&self) -> Option<&[Self::Column]> {
1059            self.column.as_deref()
1060        }
1061
1062        fn select(&self) -> Option<&[Self::Select]> {
1063            self.select.as_deref()
1064        }
1065
1066        fn for_each(&self) -> Option<&str> {
1067            self.for_each.as_ref()?.value.as_deref()
1068        }
1069
1070        fn for_each_or_null(&self) -> Option<&str> {
1071            self.for_each_or_null.as_ref()?.value.as_deref()
1072        }
1073
1074        fn union_all(&self) -> Option<&[Self::Select]> {
1075            self.union_all.as_deref()
1076        }
1077    }
1078
1079    impl ViewDefinitionColumnTrait for ViewDefinitionSelectColumn {
1080        fn name(&self) -> Option<&str> {
1081            self.name.value.as_deref()
1082        }
1083
1084        fn path(&self) -> Option<&str> {
1085            self.path.value.as_deref()
1086        }
1087
1088        fn collection(&self) -> Option<bool> {
1089            self.collection.as_ref()?.value
1090        }
1091    }
1092
1093    impl ViewDefinitionWhereTrait for ViewDefinitionWhere {
1094        fn path(&self) -> Option<&str> {
1095            self.path.value.as_deref()
1096        }
1097    }
1098
1099    impl ViewDefinitionConstantTrait for ViewDefinitionConstant {
1100        fn name(&self) -> Option<&str> {
1101            self.name.value.as_deref()
1102        }
1103
1104        fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError> {
1105            let name = self.name().unwrap_or("unknown");
1106
1107            if let Some(value) = &self.value {
1108                // R5 implementation identical to R4
1109                let eval_result = match value {
1110                    ViewDefinitionConstantValue::String(s) => {
1111                        EvaluationResult::String(s.value.clone().unwrap_or_default(), None)
1112                    }
1113                    ViewDefinitionConstantValue::Boolean(b) => {
1114                        EvaluationResult::Boolean(b.value.unwrap_or(false), None)
1115                    }
1116                    ViewDefinitionConstantValue::Integer(i) => {
1117                        EvaluationResult::Integer(i.value.unwrap_or(0) as i64, None)
1118                    }
1119                    ViewDefinitionConstantValue::Decimal(d) => {
1120                        if let Some(precise_decimal) = &d.value {
1121                            match precise_decimal.original_string().parse() {
1122                                Ok(decimal_value) => EvaluationResult::Decimal(decimal_value, None),
1123                                Err(_) => {
1124                                    return Err(SofError::InvalidViewDefinition(format!(
1125                                        "Invalid decimal value for constant '{}'",
1126                                        name
1127                                    )));
1128                                }
1129                            }
1130                        } else {
1131                            EvaluationResult::Decimal("0".parse().unwrap(), None)
1132                        }
1133                    }
1134                    ViewDefinitionConstantValue::Date(d) => {
1135                        EvaluationResult::Date(d.value.clone().unwrap_or_default(), None)
1136                    }
1137                    ViewDefinitionConstantValue::DateTime(dt) => {
1138                        let value = dt.value.clone().unwrap_or_default();
1139                        // Ensure DateTime values have the "@" prefix for FHIRPath
1140                        let prefixed = if value.starts_with("@") {
1141                            value
1142                        } else {
1143                            format!("@{}", value)
1144                        };
1145                        EvaluationResult::DateTime(
1146                            prefixed,
1147                            Some(TypeInfoResult::new("FHIR", "dateTime")),
1148                        )
1149                    }
1150                    ViewDefinitionConstantValue::Time(t) => {
1151                        let value = t.value.clone().unwrap_or_default();
1152                        // Ensure Time values have the "@T" prefix for FHIRPath
1153                        let prefixed = if value.starts_with("@T") {
1154                            value
1155                        } else {
1156                            format!("@T{}", value)
1157                        };
1158                        EvaluationResult::Time(prefixed, None)
1159                    }
1160                    ViewDefinitionConstantValue::Code(c) => {
1161                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
1162                    }
1163                    ViewDefinitionConstantValue::Base64Binary(b) => {
1164                        EvaluationResult::String(b.value.clone().unwrap_or_default(), None)
1165                    }
1166                    ViewDefinitionConstantValue::Id(i) => {
1167                        EvaluationResult::String(i.value.clone().unwrap_or_default(), None)
1168                    }
1169                    ViewDefinitionConstantValue::Instant(i) => {
1170                        let value = i.value.clone().unwrap_or_default();
1171                        // Ensure Instant values have the "@" prefix for FHIRPath
1172                        let prefixed = if value.starts_with("@") {
1173                            value
1174                        } else {
1175                            format!("@{}", value)
1176                        };
1177                        EvaluationResult::DateTime(
1178                            prefixed,
1179                            Some(TypeInfoResult::new("FHIR", "instant")),
1180                        )
1181                    }
1182                    ViewDefinitionConstantValue::Oid(o) => {
1183                        EvaluationResult::String(o.value.clone().unwrap_or_default(), None)
1184                    }
1185                    ViewDefinitionConstantValue::PositiveInt(p) => {
1186                        EvaluationResult::Integer(p.value.unwrap_or(1) as i64, None)
1187                    }
1188                    ViewDefinitionConstantValue::UnsignedInt(u) => {
1189                        EvaluationResult::Integer(u.value.unwrap_or(0) as i64, None)
1190                    }
1191                    ViewDefinitionConstantValue::Uri(u) => {
1192                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
1193                    }
1194                    ViewDefinitionConstantValue::Url(u) => {
1195                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
1196                    }
1197                    ViewDefinitionConstantValue::Uuid(u) => {
1198                        EvaluationResult::String(u.value.clone().unwrap_or_default(), None)
1199                    }
1200                    ViewDefinitionConstantValue::Canonical(c) => {
1201                        EvaluationResult::String(c.value.clone().unwrap_or_default(), None)
1202                    }
1203                    ViewDefinitionConstantValue::Integer64(i) => {
1204                        EvaluationResult::Integer(i.value.unwrap_or(0), None)
1205                    }
1206                };
1207
1208                Ok(eval_result)
1209            } else {
1210                Err(SofError::InvalidViewDefinition(format!(
1211                    "Constant '{}' must have a value",
1212                    name
1213                )))
1214            }
1215        }
1216    }
1217
1218    impl BundleTrait for Bundle {
1219        type Resource = Resource;
1220
1221        fn entries(&self) -> Vec<&Self::Resource> {
1222            self.entry
1223                .as_ref()
1224                .map(|entries| {
1225                    entries
1226                        .iter()
1227                        .filter_map(|e| e.resource.as_deref()) // Note: R6 uses Box<Resource>
1228                        .collect()
1229                })
1230                .unwrap_or_default()
1231        }
1232    }
1233
1234    impl ResourceTrait for Resource {
1235        fn resource_name(&self) -> &str {
1236            self.resource_name()
1237        }
1238
1239        fn to_fhir_resource(&self) -> FhirResource {
1240            FhirResource::R6(Box::new(self.clone()))
1241        }
1242
1243        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
1244            self.get_last_updated()
1245        }
1246    }
1247}