octofhir_fhir_model/
provider.rs

1//! ModelProvider trait for FHIR model access
2//!
3//! This module provides the core ModelProvider trait for FHIRPath evaluation.
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8use async_trait::async_trait;
9
10use crate::error::Result;
11use crate::evaluation::{EvaluationResult, IntoEvaluationResult, TypeInfoResult};
12
13/// Core trait for accessing FHIR model information
14///
15/// Core trait for accessing FHIR model information during FHIRPath evaluation
16#[async_trait]
17pub trait ModelProvider: Send + Sync + std::fmt::Debug {
18    /// Core type lookup
19    async fn get_type(&self, type_name: &str) -> Result<Option<TypeInfo>>;
20
21    /// Get element type from complex type
22    async fn get_element_type(
23        &self,
24        parent_type: &TypeInfo,
25        property_name: &str,
26    ) -> Result<Option<TypeInfo>>;
27
28    /// Get type from union type
29    fn of_type(&self, type_info: &TypeInfo, target_type: &str) -> Option<TypeInfo>;
30
31    /// Get element names from complex type
32    fn get_element_names(&self, parent_type: &TypeInfo) -> Vec<String>;
33
34    /// Returns a union type of all possible child element types
35    async fn get_children_type(&self, parent_type: &TypeInfo) -> Result<Option<TypeInfo>>;
36
37    /// Get detailed information about elements of a type for completion suggestions
38    async fn get_elements(&self, type_name: &str) -> Result<Vec<ElementInfo>>;
39
40    /// Get list of all resource types
41    async fn get_resource_types(&self) -> Result<Vec<String>>;
42
43    /// Get list of all complex types
44    async fn get_complex_types(&self) -> Result<Vec<String>>;
45
46    /// Get list of all primitive types
47    async fn get_primitive_types(&self) -> Result<Vec<String>>;
48
49    /// Check if a resource type exists
50    async fn resource_type_exists(&self, resource_type: &str) -> Result<bool> {
51        let resource_types = self.get_resource_types().await?;
52        Ok(resource_types.contains(&resource_type.to_string()))
53    }
54
55    /// Get the FHIR version supported by this provider
56    async fn get_fhir_version(&self) -> Result<FhirVersion> {
57        // Default implementation returns R4
58        Ok(FhirVersion::R4)
59    }
60
61    /// Check if one type is derived from another using schema hierarchy
62    /// Default implementation - override in concrete providers with actual schema data
63    fn is_type_derived_from(&self, derived_type: &str, base_type: &str) -> bool {
64        // Default implementation for base trait - just direct equality
65        derived_type == base_type
66    }
67
68    /// Get choice type metadata for a property (valueX patterns)
69    async fn get_choice_types(
70        &self,
71        parent_type: &str,
72        property_name: &str,
73    ) -> Result<Option<Vec<ChoiceTypeInfo>>> {
74        let _ = (parent_type, property_name);
75        Ok(None)
76    }
77
78    /// Get union type information for a type
79    async fn get_union_types(&self, type_info: &TypeInfo) -> Result<Option<Vec<TypeInfo>>> {
80        let _ = type_info;
81        Ok(None)
82    }
83
84    /// Check if a type is a union type
85    fn is_union_type(&self, type_info: &TypeInfo) -> bool {
86        let _ = type_info;
87        false
88    }
89}
90
91/// Type information structure for FHIR elements
92#[derive(Debug, Clone, PartialEq)]
93#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
94pub struct TypeInfo {
95    /// FHIRPath type name ('Any', 'Boolean', 'String', 'Integer', etc.)
96    pub type_name: String,
97    /// Single value vs collection (optional, defaults to true)
98    pub singleton: Option<bool>,
99    /// Indicates this is definitely an empty collection
100    pub is_empty: Option<bool>,
101    /// Model type namespace ('FHIR', 'System', etc.)
102    pub namespace: Option<String>,
103    /// Model type name (Patient, string, etc.)
104    pub name: Option<String>,
105}
106
107impl TypeInfo {
108    /// Create a system type
109    pub fn system_type(type_name: String, singleton: bool) -> Self {
110        Self {
111            type_name: type_name.clone(),
112            singleton: Some(singleton),
113            is_empty: Some(false),
114            namespace: Some("System".to_string()),
115            name: Some(type_name),
116        }
117    }
118}
119
120/// Element information for completion suggestions
121#[derive(Debug, Clone, PartialEq)]
122#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
123pub struct ElementInfo {
124    /// Element name
125    pub name: String,
126    /// Element type
127    pub element_type: String,
128    /// Documentation/description
129    pub documentation: Option<String>,
130}
131
132/// Choice type information for valueX properties
133#[derive(Debug, Clone, PartialEq)]
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135pub struct ChoiceTypeInfo {
136    /// The suffix for the property (e.g., "String" for valueString)
137    pub suffix: String,
138    /// The FHIR type name (e.g., "string")
139    pub type_name: String,
140}
141
142/// FHIR version enumeration
143#[derive(Debug, Clone, PartialEq, Eq)]
144#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
145pub enum FhirVersion {
146    /// FHIR R4 (4.0.x)
147    R4,
148    /// FHIR R4B (4.3.x)
149    R4B,
150    /// FHIR R5 (5.0.x)
151    R5,
152    /// FHIR R6 (6.0.x)
153    R6,
154    /// Custom version
155    Custom {
156        /// Version identifier
157        version: String,
158    },
159}
160
161/// Empty implementation of ModelProvider for testing and default behavior
162#[derive(Debug, Clone, Default)]
163pub struct EmptyModelProvider;
164
165#[async_trait]
166impl ModelProvider for EmptyModelProvider {
167    async fn get_type(&self, type_name: &str) -> Result<Option<TypeInfo>> {
168        match type_name {
169            "Patient" | "Observation" | "Practitioner" | "Organization" => Ok(Some(TypeInfo {
170                type_name: "Any".to_string(),
171                singleton: Some(true),
172                is_empty: Some(false),
173                namespace: Some("FHIR".to_string()),
174                name: Some(type_name.to_string()),
175            })),
176            "Boolean" => Ok(Some(TypeInfo {
177                type_name: "Boolean".to_string(),
178                singleton: Some(true),
179                is_empty: Some(false),
180                namespace: Some("System".to_string()),
181                name: Some("Boolean".to_string()),
182            })),
183            "String" => Ok(Some(TypeInfo {
184                type_name: "String".to_string(),
185                singleton: Some(true),
186                is_empty: Some(false),
187                namespace: Some("System".to_string()),
188                name: Some("String".to_string()),
189            })),
190            "Integer" => Ok(Some(TypeInfo {
191                type_name: "Integer".to_string(),
192                singleton: Some(true),
193                is_empty: Some(false),
194                namespace: Some("System".to_string()),
195                name: Some("Integer".to_string()),
196            })),
197            "Decimal" => Ok(Some(TypeInfo {
198                type_name: "Decimal".to_string(),
199                singleton: Some(true),
200                is_empty: Some(false),
201                namespace: Some("System".to_string()),
202                name: Some("Decimal".to_string()),
203            })),
204            _ => Ok(None),
205        }
206    }
207
208    async fn get_element_type(
209        &self,
210        parent_type: &TypeInfo,
211        property_name: &str,
212    ) -> Result<Option<TypeInfo>> {
213        match (
214            parent_type
215                .name
216                .as_deref()
217                .unwrap_or(&parent_type.type_name),
218            property_name,
219        ) {
220            ("Patient", "name") => Ok(Some(TypeInfo {
221                type_name: "Any".to_string(),
222                singleton: Some(false),
223                is_empty: Some(false),
224                namespace: Some("FHIR".to_string()),
225                name: Some("HumanName".to_string()),
226            })),
227            ("HumanName", "given") => Ok(Some(TypeInfo {
228                type_name: "String".to_string(),
229                singleton: Some(false),
230                is_empty: Some(false),
231                namespace: Some("System".to_string()),
232                name: Some("String".to_string()),
233            })),
234            _ => Ok(None),
235        }
236    }
237
238    fn of_type(&self, type_info: &TypeInfo, target_type: &str) -> Option<TypeInfo> {
239        // Direct type match
240        if type_info.type_name == target_type {
241            return Some(type_info.clone());
242        }
243
244        // Name match
245        if let Some(ref name) = type_info.name {
246            if name == target_type {
247                return Some(type_info.clone());
248            }
249            // Check type hierarchy using is_type_derived_from
250            if self.is_type_derived_from(name, target_type) {
251                return Some(type_info.clone());
252            }
253        }
254
255        // Check if type_name derives from target_type
256        if self.is_type_derived_from(&type_info.type_name, target_type) {
257            return Some(type_info.clone());
258        }
259
260        None
261    }
262
263    fn is_type_derived_from(&self, derived_type: &str, base_type: &str) -> bool {
264        if derived_type == base_type {
265            return true;
266        }
267
268        // Minimal type hierarchy for testing - in real implementation this comes from schemas
269        matches!(
270            (derived_type, base_type),
271            ("code" | "id" | "uri", "string")
272                | ("Patient", "DomainResource")
273                | ("DomainResource", "Resource")
274        )
275    }
276
277    fn get_element_names(&self, parent_type: &TypeInfo) -> Vec<String> {
278        match parent_type
279            .name
280            .as_deref()
281            .unwrap_or(&parent_type.type_name)
282        {
283            "Patient" => vec![
284                "id".to_string(),
285                "name".to_string(),
286                "gender".to_string(),
287                "birthDate".to_string(),
288            ],
289            "HumanName" => vec!["given".to_string(), "family".to_string(), "use".to_string()],
290            "Observation" => vec![
291                "id".to_string(),
292                "status".to_string(),
293                "code".to_string(),
294                "value".to_string(),
295                "subject".to_string(),
296            ],
297            _ => Vec::new(),
298        }
299    }
300
301    async fn get_children_type(&self, parent_type: &TypeInfo) -> Result<Option<TypeInfo>> {
302        if parent_type.singleton.unwrap_or(true) {
303            Ok(None)
304        } else {
305            Ok(Some(TypeInfo {
306                type_name: parent_type.type_name.clone(),
307                singleton: Some(true),
308                is_empty: Some(false),
309                namespace: parent_type.namespace.clone(),
310                name: parent_type.name.clone(),
311            }))
312        }
313    }
314
315    async fn get_elements(&self, type_name: &str) -> Result<Vec<ElementInfo>> {
316        match type_name {
317            "Patient" => Ok(vec![
318                ElementInfo {
319                    name: "id".to_string(),
320                    element_type: "id".to_string(),
321                    documentation: Some("Logical id of this artifact".to_string()),
322                },
323                ElementInfo {
324                    name: "name".to_string(),
325                    element_type: "HumanName[]".to_string(),
326                    documentation: Some("A name associated with the patient".to_string()),
327                },
328            ]),
329            _ => Ok(Vec::new()),
330        }
331    }
332
333    async fn get_resource_types(&self) -> Result<Vec<String>> {
334        Ok(vec![
335            "Patient".to_string(),
336            "Observation".to_string(),
337            "Practitioner".to_string(),
338            "Organization".to_string(),
339        ])
340    }
341
342    async fn get_complex_types(&self) -> Result<Vec<String>> {
343        Ok(vec![
344            "HumanName".to_string(),
345            "Address".to_string(),
346            "ContactPoint".to_string(),
347            "CodeableConcept".to_string(),
348            "Quantity".to_string(),
349        ])
350    }
351
352    async fn get_primitive_types(&self) -> Result<Vec<String>> {
353        Ok(vec![
354            "Boolean".to_string(),
355            "String".to_string(),
356            "Integer".to_string(),
357            "Decimal".to_string(),
358            "Date".to_string(),
359            "DateTime".to_string(),
360            "Time".to_string(),
361        ])
362    }
363
364    async fn get_choice_types(
365        &self,
366        parent_type: &str,
367        property_name: &str,
368    ) -> Result<Option<Vec<ChoiceTypeInfo>>> {
369        match (parent_type, property_name) {
370            ("Observation", "value") => Ok(Some(vec![
371                ChoiceTypeInfo {
372                    suffix: "String".to_string(),
373                    type_name: "string".to_string(),
374                },
375                ChoiceTypeInfo {
376                    suffix: "Integer".to_string(),
377                    type_name: "integer".to_string(),
378                },
379                ChoiceTypeInfo {
380                    suffix: "Boolean".to_string(),
381                    type_name: "boolean".to_string(),
382                },
383                ChoiceTypeInfo {
384                    suffix: "Quantity".to_string(),
385                    type_name: "Quantity".to_string(),
386                },
387                ChoiceTypeInfo {
388                    suffix: "CodeableConcept".to_string(),
389                    type_name: "CodeableConcept".to_string(),
390                },
391            ])),
392            _ => Ok(None),
393        }
394    }
395
396    async fn get_union_types(&self, type_info: &TypeInfo) -> Result<Option<Vec<TypeInfo>>> {
397        match type_info.type_name.as_str() {
398            "Union" | "Choice" => Ok(Some(vec![
399                TypeInfo {
400                    type_name: "String".to_string(),
401                    singleton: Some(true),
402                    is_empty: Some(false),
403                    namespace: Some("System".to_string()),
404                    name: Some("String".to_string()),
405                },
406                TypeInfo {
407                    type_name: "Integer".to_string(),
408                    singleton: Some(true),
409                    is_empty: Some(false),
410                    namespace: Some("System".to_string()),
411                    name: Some("Integer".to_string()),
412                },
413            ])),
414            _ => Ok(None),
415        }
416    }
417
418    fn is_union_type(&self, type_info: &TypeInfo) -> bool {
419        matches!(type_info.type_name.as_str(), "Union" | "Choice")
420    }
421}
422
423impl std::fmt::Display for FhirVersion {
424    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
425        match self {
426            FhirVersion::R4 => write!(f, "R4"),
427            FhirVersion::R4B => write!(f, "R4B"),
428            FhirVersion::R5 => write!(f, "R5"),
429            FhirVersion::R6 => write!(f, "R6"),
430            FhirVersion::Custom { version } => write!(f, "{version}"),
431        }
432    }
433}
434
435// === IntoEvaluationResult implementations ===
436
437impl IntoEvaluationResult for TypeInfo {
438    fn to_evaluation_result(&self) -> EvaluationResult {
439        // Convert TypeInfo to an object representation
440        let mut map = std::collections::HashMap::new();
441
442        map.insert(
443            "type_name".to_string(),
444            self.type_name.to_evaluation_result(),
445        );
446
447        if let Some(singleton) = self.singleton {
448            map.insert("singleton".to_string(), singleton.to_evaluation_result());
449        }
450
451        if let Some(is_empty) = self.is_empty {
452            map.insert("is_empty".to_string(), is_empty.to_evaluation_result());
453        }
454
455        if let Some(ref namespace) = self.namespace {
456            map.insert("namespace".to_string(), namespace.to_evaluation_result());
457        }
458
459        if let Some(ref name) = self.name {
460            map.insert("name".to_string(), name.to_evaluation_result());
461        }
462
463        let type_info = if let Some(ref namespace) = self.namespace {
464            Some(TypeInfoResult::new(namespace, &self.type_name))
465        } else {
466            Some(TypeInfoResult::system(&self.type_name))
467        };
468
469        EvaluationResult::Object { map, type_info }
470    }
471}
472
473impl IntoEvaluationResult for ElementInfo {
474    fn to_evaluation_result(&self) -> EvaluationResult {
475        let mut map = std::collections::HashMap::new();
476
477        map.insert("name".to_string(), self.name.to_evaluation_result());
478        map.insert(
479            "element_type".to_string(),
480            self.element_type.to_evaluation_result(),
481        );
482
483        if let Some(ref documentation) = self.documentation {
484            map.insert(
485                "documentation".to_string(),
486                documentation.to_evaluation_result(),
487            );
488        }
489
490        EvaluationResult::typed_object(map, "FHIR", "ElementInfo")
491    }
492}
493
494impl IntoEvaluationResult for ChoiceTypeInfo {
495    fn to_evaluation_result(&self) -> EvaluationResult {
496        let mut map = std::collections::HashMap::new();
497
498        map.insert("suffix".to_string(), self.suffix.to_evaluation_result());
499        map.insert(
500            "type_name".to_string(),
501            self.type_name.to_evaluation_result(),
502        );
503
504        EvaluationResult::typed_object(map, "FHIR", "ChoiceTypeInfo")
505    }
506}
507
508impl IntoEvaluationResult for FhirVersion {
509    fn to_evaluation_result(&self) -> EvaluationResult {
510        EvaluationResult::string(self.to_string())
511    }
512}
513
514/// Lightweight ModelProvider wrapper that delegates to a full provider
515/// but excludes validation-related functionality to break circular dependencies.
516///
517/// This provider is used in scenarios where we need basic type information
518/// without profile validation capabilities, preventing circular dependencies
519/// between ModelProvider and FhirPathEvaluator.
520#[derive(Debug, Clone)]
521pub struct LiteModelProvider {
522    /// The underlying full model provider
523    inner: std::sync::Arc<dyn ModelProvider>,
524}
525
526impl LiteModelProvider {
527    /// Create a new lite provider wrapping a full provider
528    pub fn new(inner: std::sync::Arc<dyn ModelProvider>) -> Self {
529        Self { inner }
530    }
531
532    /// Get reference to the underlying provider
533    pub fn inner(&self) -> &dyn ModelProvider {
534        self.inner.as_ref()
535    }
536
537    /// Unwrap to get the underlying provider
538    pub fn into_inner(self) -> std::sync::Arc<dyn ModelProvider> {
539        self.inner
540    }
541
542    /// Check if this provider supports enhanced validation
543    /// (always returns false for lite provider)
544    pub fn supports_validation(&self) -> bool {
545        false
546    }
547}
548
549#[async_trait]
550impl ModelProvider for LiteModelProvider {
551    async fn get_type(&self, type_name: &str) -> Result<Option<TypeInfo>> {
552        self.inner.get_type(type_name).await
553    }
554
555    async fn get_element_type(
556        &self,
557        parent_type: &TypeInfo,
558        property_name: &str,
559    ) -> Result<Option<TypeInfo>> {
560        self.inner
561            .get_element_type(parent_type, property_name)
562            .await
563    }
564
565    fn of_type(&self, type_info: &TypeInfo, target_type: &str) -> Option<TypeInfo> {
566        self.inner.of_type(type_info, target_type)
567    }
568
569    fn get_element_names(&self, parent_type: &TypeInfo) -> Vec<String> {
570        self.inner.get_element_names(parent_type)
571    }
572
573    async fn get_children_type(&self, parent_type: &TypeInfo) -> Result<Option<TypeInfo>> {
574        self.inner.get_children_type(parent_type).await
575    }
576
577    async fn get_elements(&self, type_name: &str) -> Result<Vec<ElementInfo>> {
578        self.inner.get_elements(type_name).await
579    }
580
581    async fn get_resource_types(&self) -> Result<Vec<String>> {
582        self.inner.get_resource_types().await
583    }
584
585    async fn get_complex_types(&self) -> Result<Vec<String>> {
586        self.inner.get_complex_types().await
587    }
588
589    async fn get_primitive_types(&self) -> Result<Vec<String>> {
590        self.inner.get_primitive_types().await
591    }
592
593    async fn resource_type_exists(&self, resource_type: &str) -> Result<bool> {
594        self.inner.resource_type_exists(resource_type).await
595    }
596
597    async fn get_fhir_version(&self) -> Result<FhirVersion> {
598        self.inner.get_fhir_version().await
599    }
600
601    fn is_type_derived_from(&self, derived_type: &str, base_type: &str) -> bool {
602        self.inner.is_type_derived_from(derived_type, base_type)
603    }
604
605    async fn get_choice_types(
606        &self,
607        parent_type: &str,
608        property_name: &str,
609    ) -> Result<Option<Vec<ChoiceTypeInfo>>> {
610        self.inner
611            .get_choice_types(parent_type, property_name)
612            .await
613    }
614
615    async fn get_union_types(&self, type_info: &TypeInfo) -> Result<Option<Vec<TypeInfo>>> {
616        self.inner.get_union_types(type_info).await
617    }
618
619    fn is_union_type(&self, type_info: &TypeInfo) -> bool {
620        self.inner.is_union_type(type_info)
621    }
622}