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