Skip to main content

rh_codegen/
metadata.rs

1//! FHIR type metadata structures
2//!
3//! This module defines the data structures used for FHIR type metadata that will be
4//! generated into the target crate's metadata.rs file.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// FHIR primitive types
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum FhirPrimitiveType {
12    Boolean,
13    Integer,
14    String,
15    Date,
16    DateTime,
17    Instant,
18    Time,
19    Decimal,
20    Uri,
21    Url,
22    Canonical,
23    Code,
24    Oid,
25    Id,
26    Markdown,
27    Base64Binary,
28    UnsignedInt,
29    PositiveInt,
30}
31
32impl FhirPrimitiveType {
33    /// Convert FHIR type string to FhirPrimitiveType
34    pub fn from_fhir_type(fhir_type: &str) -> Option<Self> {
35        match fhir_type {
36            "boolean" => Some(Self::Boolean),
37            "integer" => Some(Self::Integer),
38            "string" => Some(Self::String),
39            "date" => Some(Self::Date),
40            "dateTime" => Some(Self::DateTime),
41            "instant" => Some(Self::Instant),
42            "time" => Some(Self::Time),
43            "decimal" => Some(Self::Decimal),
44            "uri" => Some(Self::Uri),
45            "url" => Some(Self::Url),
46            "canonical" => Some(Self::Canonical),
47            "code" => Some(Self::Code),
48            "oid" => Some(Self::Oid),
49            "id" => Some(Self::Id),
50            "markdown" => Some(Self::Markdown),
51            "base64Binary" => Some(Self::Base64Binary),
52            "unsignedInt" => Some(Self::UnsignedInt),
53            "positiveInt" => Some(Self::PositiveInt),
54            _ => None,
55        }
56    }
57
58    /// Get the enum variant name as a string for code generation
59    pub fn variant_name(&self) -> &'static str {
60        match self {
61            Self::Boolean => "Boolean",
62            Self::Integer => "Integer",
63            Self::String => "String",
64            Self::Date => "Date",
65            Self::DateTime => "DateTime",
66            Self::Instant => "Instant",
67            Self::Time => "Time",
68            Self::Decimal => "Decimal",
69            Self::Uri => "Uri",
70            Self::Url => "Url",
71            Self::Canonical => "Canonical",
72            Self::Code => "Code",
73            Self::Oid => "Oid",
74            Self::Id => "Id",
75            Self::Markdown => "Markdown",
76            Self::Base64Binary => "Base64Binary",
77            Self::UnsignedInt => "UnsignedInt",
78            Self::PositiveInt => "PositiveInt",
79        }
80    }
81}
82
83/// FHIR field type (primitive, complex, reference, or backbone element)
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85pub enum FhirFieldType {
86    Primitive(FhirPrimitiveType),
87    Complex(String),         // e.g., "HumanName", "Address"
88    Reference,               // Reference to another resource
89    BackboneElement(String), // Nested structure defined within the parent
90}
91
92/// Information about a field in a FHIR resource or datatype
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct FieldInfo {
95    pub field_type: FhirFieldType,
96    pub min: u32,
97    pub max: Option<u32>, // None = unbounded (*)
98    pub is_choice_type: bool,
99    pub choice_types: Vec<String>, // If choice type, all possible types (e.g., ["boolean", "dateTime"])
100}
101
102/// Metadata for a FHIR type (resource or datatype)
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct TypeMetadata {
105    pub name: String,
106    pub fields: HashMap<String, FieldInfo>,
107}
108
109/// Complete metadata registry for all FHIR types
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct MetadataRegistry {
112    pub types: HashMap<String, TypeMetadata>,
113}
114
115impl MetadataRegistry {
116    pub fn new() -> Self {
117        Self {
118            types: HashMap::new(),
119        }
120    }
121
122    pub fn add_type(&mut self, type_metadata: TypeMetadata) {
123        self.types.insert(type_metadata.name.clone(), type_metadata);
124    }
125
126    /// Resolve a path like "Patient.name.given" to its field type
127    pub fn resolve_path(&self, path: &str) -> Option<&FhirFieldType> {
128        let parts: Vec<&str> = path.split('.').collect();
129        if parts.is_empty() {
130            return None;
131        }
132
133        // Start with the root type
134        let mut current_type_name = parts[0];
135        let mut current_type = self.types.get(current_type_name)?;
136
137        // Navigate through the path
138        for &field_name in &parts[1..] {
139            let field_info = current_type.fields.get(field_name)?;
140
141            // If this is the last part of the path, return this field's type
142            if field_name == *parts.last().unwrap() {
143                return Some(&field_info.field_type);
144            }
145
146            // Otherwise, continue navigating
147            match &field_info.field_type {
148                FhirFieldType::Complex(type_name) | FhirFieldType::BackboneElement(type_name) => {
149                    current_type_name = type_name;
150                    current_type = self.types.get(current_type_name)?;
151                }
152                FhirFieldType::Reference => {
153                    // Can't navigate further into a Reference
154                    return None;
155                }
156                FhirFieldType::Primitive(_) => {
157                    // Can't navigate further into a primitive
158                    return None;
159                }
160            }
161        }
162
163        None
164    }
165}
166
167impl Default for MetadataRegistry {
168    fn default() -> Self {
169        Self::new()
170    }
171}