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 crate::constants::ConstantValue;
24use helios_fhir::FhirResource;
25use helios_fhirpath::EvaluationResult;
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            let value = self.value.as_ref().ok_or_else(|| {
450                SofError::InvalidViewDefinition(format!("Constant '{name}' must have a value"))
451            })?;
452            r4_constant_to_neutral(value).to_evaluation_result()
453        }
454    }
455
456    fn r4_constant_to_neutral(value: &ViewDefinitionConstantValue) -> ConstantValue {
457        match value {
458            ViewDefinitionConstantValue::String(s) => {
459                ConstantValue::String(s.value.clone().unwrap_or_default())
460            }
461            ViewDefinitionConstantValue::Boolean(b) => {
462                ConstantValue::Boolean(b.value.unwrap_or(false))
463            }
464            ViewDefinitionConstantValue::Integer(i) => {
465                ConstantValue::Integer(i.value.unwrap_or(0) as i64)
466            }
467            ViewDefinitionConstantValue::PositiveInt(p) => {
468                ConstantValue::PositiveInt(p.value.unwrap_or(1) as i64)
469            }
470            ViewDefinitionConstantValue::UnsignedInt(u) => {
471                ConstantValue::UnsignedInt(u.value.unwrap_or(0) as i64)
472            }
473            ViewDefinitionConstantValue::Decimal(d) => ConstantValue::Decimal(
474                d.value
475                    .as_ref()
476                    .map(|p| p.original_string().to_string())
477                    .unwrap_or_else(|| "0".to_string()),
478            ),
479            ViewDefinitionConstantValue::Date(d) => {
480                ConstantValue::Date(d.value.clone().unwrap_or_default().to_string())
481            }
482            ViewDefinitionConstantValue::DateTime(dt) => {
483                ConstantValue::DateTime(dt.value.clone().unwrap_or_default().to_string())
484            }
485            ViewDefinitionConstantValue::Time(t) => {
486                ConstantValue::Time(t.value.clone().unwrap_or_default().to_string())
487            }
488            ViewDefinitionConstantValue::Instant(i) => {
489                ConstantValue::Instant(i.value.clone().unwrap_or_default().to_string())
490            }
491            ViewDefinitionConstantValue::Code(c) => {
492                ConstantValue::Code(c.value.clone().unwrap_or_default())
493            }
494            ViewDefinitionConstantValue::Base64Binary(b) => {
495                ConstantValue::Base64Binary(b.value.clone().unwrap_or_default())
496            }
497            ViewDefinitionConstantValue::Id(i) => {
498                ConstantValue::Identifier(i.value.clone().unwrap_or_default())
499            }
500            ViewDefinitionConstantValue::Oid(o) => {
501                ConstantValue::Identifier(o.value.clone().unwrap_or_default())
502            }
503            ViewDefinitionConstantValue::Uri(u) => {
504                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
505            }
506            ViewDefinitionConstantValue::Url(u) => {
507                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
508            }
509            ViewDefinitionConstantValue::Uuid(u) => {
510                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
511            }
512            ViewDefinitionConstantValue::Canonical(c) => {
513                ConstantValue::Identifier(c.value.clone().unwrap_or_default())
514            }
515        }
516    }
517
518    impl BundleTrait for Bundle {
519        type Resource = Resource;
520
521        fn entries(&self) -> Vec<&Self::Resource> {
522            self.entry
523                .as_ref()
524                .map(|entries| entries.iter().filter_map(|e| e.resource.as_ref()).collect())
525                .unwrap_or_default()
526        }
527    }
528
529    impl ResourceTrait for Resource {
530        fn resource_name(&self) -> &str {
531            self.resource_name()
532        }
533
534        fn to_fhir_resource(&self) -> FhirResource {
535            FhirResource::R4(Box::new(self.clone()))
536        }
537
538        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
539            self.get_last_updated()
540        }
541    }
542}
543
544/// R4B (FHIR 4.3.0) trait implementations.
545///
546/// This module implements all abstraction traits for FHIR R4B resources,
547/// providing the mapping between R4B-specific ViewDefinition structures
548/// and the version-agnostic trait interfaces.
549#[cfg(feature = "R4B")]
550mod r4b_impl {
551    use super::*;
552    use helios_fhir::r4b::*;
553
554    impl ViewDefinitionTrait for ViewDefinition {
555        type Select = ViewDefinitionSelect;
556        type Where = ViewDefinitionWhere;
557        type Constant = ViewDefinitionConstant;
558
559        fn resource(&self) -> Option<&str> {
560            self.resource.value.as_deref()
561        }
562
563        fn select(&self) -> Option<&[Self::Select]> {
564            self.select.as_deref()
565        }
566
567        fn where_clauses(&self) -> Option<&[Self::Where]> {
568            self.r#where.as_deref()
569        }
570
571        fn constants(&self) -> Option<&[Self::Constant]> {
572            self.constant.as_deref()
573        }
574    }
575
576    impl ViewDefinitionSelectTrait for ViewDefinitionSelect {
577        type Column = ViewDefinitionSelectColumn;
578        type Select = ViewDefinitionSelect;
579
580        fn column(&self) -> Option<&[Self::Column]> {
581            self.column.as_deref()
582        }
583
584        fn select(&self) -> Option<&[Self::Select]> {
585            self.select.as_deref()
586        }
587
588        fn for_each(&self) -> Option<&str> {
589            self.for_each.as_ref()?.value.as_deref()
590        }
591
592        fn for_each_or_null(&self) -> Option<&str> {
593            self.for_each_or_null.as_ref()?.value.as_deref()
594        }
595
596        fn repeat(&self) -> Option<Vec<&str>> {
597            self.repeat
598                .as_ref()
599                .map(|paths| paths.iter().filter_map(|p| p.value.as_deref()).collect())
600        }
601
602        fn union_all(&self) -> Option<&[Self::Select]> {
603            self.union_all.as_deref()
604        }
605    }
606
607    impl ViewDefinitionColumnTrait for ViewDefinitionSelectColumn {
608        fn name(&self) -> Option<&str> {
609            self.name.value.as_deref()
610        }
611
612        fn path(&self) -> Option<&str> {
613            self.path.value.as_deref()
614        }
615
616        fn collection(&self) -> Option<bool> {
617            self.collection.as_ref()?.value
618        }
619    }
620
621    impl ViewDefinitionWhereTrait for ViewDefinitionWhere {
622        fn path(&self) -> Option<&str> {
623            self.path.value.as_deref()
624        }
625    }
626
627    impl ViewDefinitionConstantTrait for ViewDefinitionConstant {
628        fn name(&self) -> Option<&str> {
629            self.name.value.as_deref()
630        }
631
632        fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError> {
633            let name = self.name().unwrap_or("unknown");
634            let value = self.value.as_ref().ok_or_else(|| {
635                SofError::InvalidViewDefinition(format!("Constant '{name}' must have a value"))
636            })?;
637            r4b_constant_to_neutral(value).to_evaluation_result()
638        }
639    }
640
641    fn r4b_constant_to_neutral(value: &ViewDefinitionConstantValue) -> ConstantValue {
642        match value {
643            ViewDefinitionConstantValue::String(s) => {
644                ConstantValue::String(s.value.clone().unwrap_or_default())
645            }
646            ViewDefinitionConstantValue::Boolean(b) => {
647                ConstantValue::Boolean(b.value.unwrap_or(false))
648            }
649            ViewDefinitionConstantValue::Integer(i) => {
650                ConstantValue::Integer(i.value.unwrap_or(0) as i64)
651            }
652            ViewDefinitionConstantValue::PositiveInt(p) => {
653                ConstantValue::PositiveInt(p.value.unwrap_or(1) as i64)
654            }
655            ViewDefinitionConstantValue::UnsignedInt(u) => {
656                ConstantValue::UnsignedInt(u.value.unwrap_or(0) as i64)
657            }
658            ViewDefinitionConstantValue::Decimal(d) => ConstantValue::Decimal(
659                d.value
660                    .as_ref()
661                    .map(|p| p.original_string().to_string())
662                    .unwrap_or_else(|| "0".to_string()),
663            ),
664            ViewDefinitionConstantValue::Date(d) => {
665                ConstantValue::Date(d.value.clone().unwrap_or_default().to_string())
666            }
667            ViewDefinitionConstantValue::DateTime(dt) => {
668                ConstantValue::DateTime(dt.value.clone().unwrap_or_default().to_string())
669            }
670            ViewDefinitionConstantValue::Time(t) => {
671                ConstantValue::Time(t.value.clone().unwrap_or_default().to_string())
672            }
673            ViewDefinitionConstantValue::Instant(i) => {
674                ConstantValue::Instant(i.value.clone().unwrap_or_default().to_string())
675            }
676            ViewDefinitionConstantValue::Code(c) => {
677                ConstantValue::Code(c.value.clone().unwrap_or_default())
678            }
679            ViewDefinitionConstantValue::Base64Binary(b) => {
680                ConstantValue::Base64Binary(b.value.clone().unwrap_or_default())
681            }
682            ViewDefinitionConstantValue::Id(i) => {
683                ConstantValue::Identifier(i.value.clone().unwrap_or_default())
684            }
685            ViewDefinitionConstantValue::Oid(o) => {
686                ConstantValue::Identifier(o.value.clone().unwrap_or_default())
687            }
688            ViewDefinitionConstantValue::Uri(u) => {
689                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
690            }
691            ViewDefinitionConstantValue::Url(u) => {
692                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
693            }
694            ViewDefinitionConstantValue::Uuid(u) => {
695                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
696            }
697            ViewDefinitionConstantValue::Canonical(c) => {
698                ConstantValue::Identifier(c.value.clone().unwrap_or_default())
699            }
700        }
701    }
702
703    impl BundleTrait for Bundle {
704        type Resource = Resource;
705
706        fn entries(&self) -> Vec<&Self::Resource> {
707            self.entry
708                .as_ref()
709                .map(|entries| entries.iter().filter_map(|e| e.resource.as_ref()).collect())
710                .unwrap_or_default()
711        }
712    }
713
714    impl ResourceTrait for Resource {
715        fn resource_name(&self) -> &str {
716            self.resource_name()
717        }
718
719        fn to_fhir_resource(&self) -> FhirResource {
720            FhirResource::R4B(Box::new(self.clone()))
721        }
722
723        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
724            self.get_last_updated()
725        }
726    }
727}
728
729/// R5 (FHIR 5.0.0) trait implementations.
730///
731/// This module implements all abstraction traits for FHIR R5 resources,
732/// providing the mapping between R5-specific ViewDefinition structures
733/// and the version-agnostic trait interfaces. R5 introduces the Integer64
734/// data type for constant values.
735#[cfg(feature = "R5")]
736mod r5_impl {
737    use super::*;
738    use helios_fhir::r5::*;
739
740    impl ViewDefinitionTrait for ViewDefinition {
741        type Select = ViewDefinitionSelect;
742        type Where = ViewDefinitionWhere;
743        type Constant = ViewDefinitionConstant;
744
745        fn resource(&self) -> Option<&str> {
746            self.resource.value.as_deref()
747        }
748
749        fn select(&self) -> Option<&[Self::Select]> {
750            self.select.as_deref()
751        }
752
753        fn where_clauses(&self) -> Option<&[Self::Where]> {
754            self.r#where.as_deref()
755        }
756
757        fn constants(&self) -> Option<&[Self::Constant]> {
758            self.constant.as_deref()
759        }
760    }
761
762    impl ViewDefinitionSelectTrait for ViewDefinitionSelect {
763        type Column = ViewDefinitionSelectColumn;
764        type Select = ViewDefinitionSelect;
765
766        fn column(&self) -> Option<&[Self::Column]> {
767            self.column.as_deref()
768        }
769
770        fn select(&self) -> Option<&[Self::Select]> {
771            self.select.as_deref()
772        }
773
774        fn for_each(&self) -> Option<&str> {
775            self.for_each.as_ref()?.value.as_deref()
776        }
777
778        fn for_each_or_null(&self) -> Option<&str> {
779            self.for_each_or_null.as_ref()?.value.as_deref()
780        }
781
782        fn repeat(&self) -> Option<Vec<&str>> {
783            self.repeat
784                .as_ref()
785                .map(|paths| paths.iter().filter_map(|p| p.value.as_deref()).collect())
786        }
787
788        fn union_all(&self) -> Option<&[Self::Select]> {
789            self.union_all.as_deref()
790        }
791    }
792
793    impl ViewDefinitionColumnTrait for ViewDefinitionSelectColumn {
794        fn name(&self) -> Option<&str> {
795            self.name.value.as_deref()
796        }
797
798        fn path(&self) -> Option<&str> {
799            self.path.value.as_deref()
800        }
801
802        fn collection(&self) -> Option<bool> {
803            self.collection.as_ref()?.value
804        }
805    }
806
807    impl ViewDefinitionWhereTrait for ViewDefinitionWhere {
808        fn path(&self) -> Option<&str> {
809            self.path.value.as_deref()
810        }
811    }
812
813    impl ViewDefinitionConstantTrait for ViewDefinitionConstant {
814        fn name(&self) -> Option<&str> {
815            self.name.value.as_deref()
816        }
817
818        fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError> {
819            let name = self.name().unwrap_or("unknown");
820            let value = self.value.as_ref().ok_or_else(|| {
821                SofError::InvalidViewDefinition(format!("Constant '{name}' must have a value"))
822            })?;
823            r5_constant_to_neutral(value).to_evaluation_result()
824        }
825    }
826
827    fn r5_constant_to_neutral(value: &ViewDefinitionConstantValue) -> ConstantValue {
828        match value {
829            ViewDefinitionConstantValue::String(s) => {
830                ConstantValue::String(s.value.clone().unwrap_or_default())
831            }
832            ViewDefinitionConstantValue::Boolean(b) => {
833                ConstantValue::Boolean(b.value.unwrap_or(false))
834            }
835            ViewDefinitionConstantValue::Integer(i) => {
836                ConstantValue::Integer(i.value.unwrap_or(0) as i64)
837            }
838            ViewDefinitionConstantValue::Integer64(i) => {
839                ConstantValue::Integer64(i.value.unwrap_or(0))
840            }
841            ViewDefinitionConstantValue::PositiveInt(p) => {
842                ConstantValue::PositiveInt(p.value.unwrap_or(1) as i64)
843            }
844            ViewDefinitionConstantValue::UnsignedInt(u) => {
845                ConstantValue::UnsignedInt(u.value.unwrap_or(0) as i64)
846            }
847            ViewDefinitionConstantValue::Decimal(d) => ConstantValue::Decimal(
848                d.value
849                    .as_ref()
850                    .map(|p| p.original_string().to_string())
851                    .unwrap_or_else(|| "0".to_string()),
852            ),
853            ViewDefinitionConstantValue::Date(d) => {
854                ConstantValue::Date(d.value.clone().unwrap_or_default().to_string())
855            }
856            ViewDefinitionConstantValue::DateTime(dt) => {
857                ConstantValue::DateTime(dt.value.clone().unwrap_or_default().to_string())
858            }
859            ViewDefinitionConstantValue::Time(t) => {
860                ConstantValue::Time(t.value.clone().unwrap_or_default().to_string())
861            }
862            ViewDefinitionConstantValue::Instant(i) => {
863                ConstantValue::Instant(i.value.clone().unwrap_or_default().to_string())
864            }
865            ViewDefinitionConstantValue::Code(c) => {
866                ConstantValue::Code(c.value.clone().unwrap_or_default())
867            }
868            ViewDefinitionConstantValue::Base64Binary(b) => {
869                ConstantValue::Base64Binary(b.value.clone().unwrap_or_default())
870            }
871            ViewDefinitionConstantValue::Id(i) => {
872                ConstantValue::Identifier(i.value.clone().unwrap_or_default())
873            }
874            ViewDefinitionConstantValue::Oid(o) => {
875                ConstantValue::Identifier(o.value.clone().unwrap_or_default())
876            }
877            ViewDefinitionConstantValue::Uri(u) => {
878                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
879            }
880            ViewDefinitionConstantValue::Url(u) => {
881                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
882            }
883            ViewDefinitionConstantValue::Uuid(u) => {
884                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
885            }
886            ViewDefinitionConstantValue::Canonical(c) => {
887                ConstantValue::Identifier(c.value.clone().unwrap_or_default())
888            }
889        }
890    }
891
892    impl BundleTrait for Bundle {
893        type Resource = Resource;
894
895        fn entries(&self) -> Vec<&Self::Resource> {
896            self.entry
897                .as_ref()
898                .map(|entries| {
899                    entries
900                        .iter()
901                        .filter_map(|e| e.resource.as_deref()) // Note: R5 uses Box<Resource>
902                        .collect()
903                })
904                .unwrap_or_default()
905        }
906    }
907
908    impl ResourceTrait for Resource {
909        fn resource_name(&self) -> &str {
910            self.resource_name()
911        }
912
913        fn to_fhir_resource(&self) -> FhirResource {
914            FhirResource::R5(Box::new(self.clone()))
915        }
916
917        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
918            self.get_last_updated()
919        }
920    }
921}
922
923/// R6 (FHIR 6.0.0) trait implementations.
924///
925/// This module implements all abstraction traits for FHIR R6 resources,
926/// providing the mapping between R6-specific ViewDefinition structures
927/// and the version-agnostic trait interfaces. R6 continues to support
928/// the Integer64 data type introduced in R5.
929#[cfg(feature = "R6")]
930mod r6_impl {
931    use super::*;
932    use helios_fhir::r6::*;
933
934    impl ViewDefinitionTrait for ViewDefinition {
935        type Select = ViewDefinitionSelect;
936        type Where = ViewDefinitionWhere;
937        type Constant = ViewDefinitionConstant;
938
939        fn resource(&self) -> Option<&str> {
940            self.resource.value.as_deref()
941        }
942
943        fn select(&self) -> Option<&[Self::Select]> {
944            self.select.as_deref()
945        }
946
947        fn where_clauses(&self) -> Option<&[Self::Where]> {
948            self.r#where.as_deref()
949        }
950
951        fn constants(&self) -> Option<&[Self::Constant]> {
952            self.constant.as_deref()
953        }
954    }
955
956    impl ViewDefinitionSelectTrait for ViewDefinitionSelect {
957        type Column = ViewDefinitionSelectColumn;
958        type Select = ViewDefinitionSelect;
959
960        fn column(&self) -> Option<&[Self::Column]> {
961            self.column.as_deref()
962        }
963
964        fn select(&self) -> Option<&[Self::Select]> {
965            self.select.as_deref()
966        }
967
968        fn for_each(&self) -> Option<&str> {
969            self.for_each.as_ref()?.value.as_deref()
970        }
971
972        fn for_each_or_null(&self) -> Option<&str> {
973            self.for_each_or_null.as_ref()?.value.as_deref()
974        }
975
976        fn repeat(&self) -> Option<Vec<&str>> {
977            self.repeat
978                .as_ref()
979                .map(|paths| paths.iter().filter_map(|p| p.value.as_deref()).collect())
980        }
981
982        fn union_all(&self) -> Option<&[Self::Select]> {
983            self.union_all.as_deref()
984        }
985    }
986
987    impl ViewDefinitionColumnTrait for ViewDefinitionSelectColumn {
988        fn name(&self) -> Option<&str> {
989            self.name.value.as_deref()
990        }
991
992        fn path(&self) -> Option<&str> {
993            self.path.value.as_deref()
994        }
995
996        fn collection(&self) -> Option<bool> {
997            self.collection.as_ref()?.value
998        }
999    }
1000
1001    impl ViewDefinitionWhereTrait for ViewDefinitionWhere {
1002        fn path(&self) -> Option<&str> {
1003            self.path.value.as_deref()
1004        }
1005    }
1006
1007    impl ViewDefinitionConstantTrait for ViewDefinitionConstant {
1008        fn name(&self) -> Option<&str> {
1009            self.name.value.as_deref()
1010        }
1011
1012        fn to_evaluation_result(&self) -> Result<EvaluationResult, SofError> {
1013            let name = self.name().unwrap_or("unknown");
1014            let value = self.value.as_ref().ok_or_else(|| {
1015                SofError::InvalidViewDefinition(format!("Constant '{name}' must have a value"))
1016            })?;
1017            r6_constant_to_neutral(value).to_evaluation_result()
1018        }
1019    }
1020
1021    fn r6_constant_to_neutral(value: &ViewDefinitionConstantValue) -> ConstantValue {
1022        match value {
1023            ViewDefinitionConstantValue::String(s) => {
1024                ConstantValue::String(s.value.clone().unwrap_or_default())
1025            }
1026            ViewDefinitionConstantValue::Boolean(b) => {
1027                ConstantValue::Boolean(b.value.unwrap_or(false))
1028            }
1029            ViewDefinitionConstantValue::Integer(i) => {
1030                ConstantValue::Integer(i.value.unwrap_or(0) as i64)
1031            }
1032            ViewDefinitionConstantValue::Integer64(i) => {
1033                ConstantValue::Integer64(i.value.unwrap_or(0))
1034            }
1035            ViewDefinitionConstantValue::PositiveInt(p) => {
1036                ConstantValue::PositiveInt(p.value.unwrap_or(1) as i64)
1037            }
1038            ViewDefinitionConstantValue::UnsignedInt(u) => {
1039                ConstantValue::UnsignedInt(u.value.unwrap_or(0) as i64)
1040            }
1041            ViewDefinitionConstantValue::Decimal(d) => ConstantValue::Decimal(
1042                d.value
1043                    .as_ref()
1044                    .map(|p| p.original_string().to_string())
1045                    .unwrap_or_else(|| "0".to_string()),
1046            ),
1047            ViewDefinitionConstantValue::Date(d) => {
1048                ConstantValue::Date(d.value.clone().unwrap_or_default().to_string())
1049            }
1050            ViewDefinitionConstantValue::DateTime(dt) => {
1051                ConstantValue::DateTime(dt.value.clone().unwrap_or_default().to_string())
1052            }
1053            ViewDefinitionConstantValue::Time(t) => {
1054                ConstantValue::Time(t.value.clone().unwrap_or_default().to_string())
1055            }
1056            ViewDefinitionConstantValue::Instant(i) => {
1057                ConstantValue::Instant(i.value.clone().unwrap_or_default().to_string())
1058            }
1059            ViewDefinitionConstantValue::Code(c) => {
1060                ConstantValue::Code(c.value.clone().unwrap_or_default())
1061            }
1062            ViewDefinitionConstantValue::Base64Binary(b) => {
1063                ConstantValue::Base64Binary(b.value.clone().unwrap_or_default())
1064            }
1065            ViewDefinitionConstantValue::Id(i) => {
1066                ConstantValue::Identifier(i.value.clone().unwrap_or_default())
1067            }
1068            ViewDefinitionConstantValue::Oid(o) => {
1069                ConstantValue::Identifier(o.value.clone().unwrap_or_default())
1070            }
1071            ViewDefinitionConstantValue::Uri(u) => {
1072                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
1073            }
1074            ViewDefinitionConstantValue::Url(u) => {
1075                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
1076            }
1077            ViewDefinitionConstantValue::Uuid(u) => {
1078                ConstantValue::Identifier(u.value.clone().unwrap_or_default())
1079            }
1080            ViewDefinitionConstantValue::Canonical(c) => {
1081                ConstantValue::Identifier(c.value.clone().unwrap_or_default())
1082            }
1083        }
1084    }
1085
1086    impl BundleTrait for Bundle {
1087        type Resource = Resource;
1088
1089        fn entries(&self) -> Vec<&Self::Resource> {
1090            self.entry
1091                .as_ref()
1092                .map(|entries| {
1093                    entries
1094                        .iter()
1095                        .filter_map(|e| e.resource.as_deref()) // Note: R6 uses Box<Resource>
1096                        .collect()
1097                })
1098                .unwrap_or_default()
1099        }
1100    }
1101
1102    impl ResourceTrait for Resource {
1103        fn resource_name(&self) -> &str {
1104            self.resource_name()
1105        }
1106
1107        fn to_fhir_resource(&self) -> FhirResource {
1108            FhirResource::R6(Box::new(self.clone()))
1109        }
1110
1111        fn get_last_updated(&self) -> Option<chrono::DateTime<chrono::Utc>> {
1112            self.get_last_updated()
1113        }
1114    }
1115}