Skip to main content

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