Skip to main content

haste_codegen/
utilities.rs

1#![allow(unused)]
2use std::{
3    collections::{HashMap, HashSet},
4    sync::LazyLock,
5};
6
7/// Some of these keywords are present as properties in the FHIR spec.
8/// We need to prefix them with an underscore to avoid conflicts.
9/// And use an attribute to rename the field in the generated code.
10pub static RUST_KEYWORDS: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
11    let mut m = HashSet::new();
12    m.insert("self");
13    m.insert("Self");
14    m.insert("super");
15    m.insert("type");
16    m.insert("use");
17    m.insert("identifier");
18    m.insert("abstract");
19    m.insert("for");
20    m.insert("if");
21    m.insert("else");
22    m.insert("match");
23    m.insert("while");
24    m.insert("loop");
25    m.insert("break");
26    m.insert("continue");
27    m.insert("ref");
28    m.insert("return");
29    m.insert("async");
30    m.insert("where");
31    m.insert("in");
32    m.insert("final");
33    m.insert("as");
34    m.insert("do");
35    m.insert("box");
36    m.insert("pub");
37    m.insert("false");
38    m.insert("true");
39    m.insert("mod");
40    m.insert("gen");
41    m.insert("crate");
42    m.insert("fn");
43    m.insert("let");
44    m.insert("const");
45    m.insert("static");
46    m.insert("struct");
47    m.insert("enum");
48    m.insert("trait");
49    m.insert("impl");
50    m.insert("unsafe");
51    m.insert("extern");
52    m.insert("move");
53    m.insert("mut");
54    m.insert("dyn");
55    m.insert("await");
56    m.insert("try");
57    m.insert("yield");
58    m.insert("macro");
59    m.insert("union");
60    m
61});
62
63pub static RUST_PRIMITIVES: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
64    let mut m = HashMap::new();
65    m.insert(
66        "http://hl7.org/fhirpath/System.String".to_string(),
67        "String".to_string(),
68    );
69    m.insert(
70        "http://hl7.org/fhirpath/System.Decimal".to_string(),
71        "f64".to_string(),
72    );
73    m.insert(
74        "http://hl7.org/fhirpath/System.Boolean".to_string(),
75        "bool".to_string(),
76    );
77    m.insert(
78        "http://hl7.org/fhirpath/System.Integer".to_string(),
79        "i64".to_string(),
80    );
81    m.insert(
82        "http://hl7.org/fhirpath/System.Time".to_string(),
83        "crate::r4::datetime::Time".to_string(),
84    );
85    m.insert(
86        "http://hl7.org/fhirpath/System.Date".to_string(),
87        "crate::r4::datetime::Date".to_string(),
88    );
89    m.insert(
90        "http://hl7.org/fhirpath/System.DateTime".to_string(),
91        "crate::r4::datetime::DateTime".to_string(),
92    );
93    m.insert(
94        "http://hl7.org/fhirpath/System.Instant".to_string(),
95        "crate::r4::datetime::Instant".to_string(),
96    );
97    m
98});
99
100pub static FHIR_PRIMITIVES: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
101    let mut m = HashMap::new();
102    // bool type
103    m.insert("boolean".to_string(), "FHIRBoolean".to_string());
104
105    // f64 type
106    m.insert("decimal".to_string(), "FHIRDecimal".to_string());
107
108    // i64 type
109    m.insert("integer".to_string(), "FHIRInteger".to_string());
110    // u64 type
111    m.insert("positiveInt".to_string(), "FHIRPositiveInt".to_string());
112    m.insert("unsignedInt".to_string(), "FHIRUnsignedInt".to_string());
113
114    // String type
115    m.insert("base64Binary".to_string(), "FHIRBase64Binary".to_string());
116    m.insert("canonical".to_string(), "FHIRCanonical".to_string());
117    m.insert("code".to_string(), "FHIRCode".to_string());
118    m.insert("id".to_string(), "FHIRId".to_string());
119    m.insert("markdown".to_string(), "FHIRMarkdown".to_string());
120    m.insert("oid".to_string(), "FHIROid".to_string());
121    m.insert("string".to_string(), "FHIRString".to_string());
122    m.insert("uri".to_string(), "FHIRUri".to_string());
123    m.insert("url".to_string(), "FHIRUrl".to_string());
124    m.insert("uuid".to_string(), "FHIRUuid".to_string());
125    m.insert("xhtml".to_string(), "FHIRXhtml".to_string());
126
127    // Date and Time types
128    m.insert("instant".to_string(), "FHIRInstant".to_string());
129    m.insert("date".to_string(), "FHIRDate".to_string());
130    m.insert("dateTime".to_string(), "FHIRDateTime".to_string());
131    m.insert("time".to_string(), "FHIRTime".to_string());
132
133    m
134});
135
136pub static FHIR_PRIMITIVE_VALUE_TYPE: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
137    let mut m = HashMap::new();
138    // bool type
139    m.insert("boolean".to_string(), "bool".to_string());
140
141    // f64 type
142    m.insert("decimal".to_string(), "f64".to_string());
143
144    // i64 type
145    m.insert("integer".to_string(), "i64".to_string());
146    // u64 type
147    m.insert("positiveInt".to_string(), "u64".to_string());
148    m.insert("unsignedInt".to_string(), "u64".to_string());
149
150    // String type
151    m.insert("base64Binary".to_string(), "String".to_string());
152    m.insert("canonical".to_string(), "String".to_string());
153    m.insert("code".to_string(), "String".to_string());
154    m.insert("date".to_string(), "String".to_string());
155    m.insert("dateTime".to_string(), "String".to_string());
156    m.insert("id".to_string(), "String".to_string());
157    m.insert("instant".to_string(), "String".to_string());
158    m.insert("markdown".to_string(), "String".to_string());
159    m.insert("oid".to_string(), "String".to_string());
160    m.insert("string".to_string(), "String".to_string());
161    m.insert("time".to_string(), "String".to_string());
162    m.insert("uri".to_string(), "String".to_string());
163    m.insert("url".to_string(), "String".to_string());
164    m.insert("uuid".to_string(), "String".to_string());
165    m.insert("xhtml".to_string(), "String".to_string());
166
167    m
168});
169
170pub mod conversion {
171    use std::collections::HashMap;
172
173    use super::{FHIR_PRIMITIVES, RUST_PRIMITIVES};
174    use haste_fhir_model::r4::generated::{terminology::BindingStrength, types::ElementDefinition};
175    use proc_macro2::TokenStream;
176    use quote::{format_ident, quote};
177
178    /// Converts a FHIR type to its corresponding Rust type.
179    ///
180    /// Returns a tuple where:
181    /// - The first element is the Rust type as a `TokenStream`.
182    /// - The second element indicates whether the returned type is a generated
183    ///   FHIR type (`true`) or a built-in Rust type (`false`).
184    ///
185    /// This function performs special handling for:
186    /// - `unsignedInt.value` and `positiveInt.value`, which map to `u64`.
187    /// - `instant.value`, which maps to the Rust `Instant` type.
188    /// - Primitive FHIR types with required bindings that have been inlined into
189    ///   generated terminology types.
190    ///
191    /// # Panics
192    ///
193    /// Panics if the internal `RUST_PRIMITIVES` mapping does not contain the
194    /// `http://hl7.org/fhirpath/System.Instant` entry, or if any primitive type
195    /// mapping stored in `RUST_PRIMITIVES` is not a valid `TokenStream`.
196    pub fn fhir_type_to_rust_type<S: std::hash::BuildHasher>(
197        element: &ElementDefinition,
198        fhir_type: &str,
199        inlined_terminology: &HashMap<String, String, S>,
200    ) -> (TokenStream, bool) {
201        let path = element.path.value.as_deref();
202
203        match path {
204            Some("unsignedInt.value" | "positiveInt.value") => {
205                let k = format_ident!("{}", "u64");
206                (
207                    quote! {
208                        #k
209                    },
210                    false,
211                )
212            }
213
214            _ => {
215                if let Some(rust_primitive) = RUST_PRIMITIVES.get(fhir_type) {
216                    if matches!(path, Some("instant.value")) {
217                        let k = RUST_PRIMITIVES
218                            .get("http://hl7.org/fhirpath/System.Instant")
219                            .unwrap()
220                            .parse::<TokenStream>()
221                            .unwrap();
222
223                        (
224                            quote! {
225                                #k
226                            },
227                            false,
228                        )
229                    } else {
230                        let k = rust_primitive.parse::<TokenStream>().unwrap();
231                        (
232                            quote! {
233                                #k
234                            },
235                            false,
236                        )
237                    }
238                } else if let Some(primitive) = FHIR_PRIMITIVES.get(fhir_type) {
239                    // Support for inlined types.
240                    // inlined could be a url | version for canonical.
241                    // Only do inlined if the binding is required and exists as inlined terminology.
242
243                    if Some(&BindingStrength::required())
244                        == element.binding.as_ref().map(|b| &b.strength)
245                        && let Some(canonical_string) = element
246                            .binding
247                            .as_ref()
248                            .and_then(|b| b.valueSet.as_ref())
249                            .and_then(|b| b.value.as_ref())
250                            .map(std::string::String::as_str)
251                        && let Some(url) = canonical_string.split('|').next()
252                        && let Some(inlined) = inlined_terminology.get(url)
253                    {
254                        let inline_type = format_ident!("{}", inlined);
255                        (
256                            quote! {
257                                terminology::BoundCode<terminology::#inline_type>
258                            },
259                            false,
260                        )
261                    } else {
262                        let k = format_ident!("{}", primitive.clone());
263                        (
264                            quote! {
265                                #k
266                            },
267                            true,
268                        )
269                    }
270                } else {
271                    let k = format_ident!("{}", fhir_type.to_string());
272                    (
273                        quote! {
274                            #k
275                        },
276                        true,
277                    )
278                }
279            }
280        }
281    }
282}
283
284pub mod extract {
285    use haste_fhir_model::r4::generated::resources::StructureDefinition;
286    use haste_fhir_model::r4::generated::types::ElementDefinition;
287    pub fn field_types(element: &ElementDefinition) -> Vec<&str> {
288        element.type_.as_ref().map_or_else(Vec::new, |types| {
289            types
290                .iter()
291                .filter_map(|t| t.code.value.as_deref())
292                .collect()
293        })
294    }
295
296    #[must_use]
297    pub fn field_name(path: &str) -> String {
298        let field_name: String = path
299            .split('.')
300            .next_back()
301            .unwrap_or("")
302            .chars()
303            .enumerate()
304            .map(|(i, c)| {
305                if i == 0 {
306                    c.to_lowercase().next().unwrap_or(c)
307                } else {
308                    c
309                }
310            })
311            .collect();
312        if field_name.ends_with("[x]") {
313            field_name.replace("[x]", "")
314        } else {
315            field_name.clone()
316        }
317    }
318
319    pub fn is_abstract(sd: &StructureDefinition) -> bool {
320        sd.abstract_.value == Some(true)
321    }
322
323    pub fn path(element: &ElementDefinition) -> String {
324        element.path.value.clone().unwrap_or_default()
325    }
326    pub fn element_description(element: &ElementDefinition) -> String {
327        element
328            .definition
329            .as_ref()
330            .and_then(|d| d.value.as_ref())
331            .cloned()
332            .unwrap_or_else(|| {
333                element
334                    .path
335                    .value
336                    .clone()
337                    .unwrap_or_else(|| "no description".to_string())
338            })
339    }
340
341    /// Returns the FHIR type associated with an element.
342    ///
343    /// For the root element of a [`StructureDefinition`], the type is taken from
344    /// the structure definition itself (`StructureDefinition.type_`).
345    ///
346    /// For all other elements, this function expects exactly one declared FHIR type
347    /// in `ElementDefinition.type_` and returns its code.
348    ///
349    /// # Panics
350    ///
351    /// This function will panic if:
352    /// - The root element does not have a `StructureDefinition.type_`.
353    /// - A non-root element has no type code.
354    /// - A non-root element has multiple declared types, as the FHIR type would be
355    ///   ambiguous.
356    ///
357    /// # Arguments
358    ///
359    /// * `sd` - The [`StructureDefinition`] containing the element.
360    /// * `element` - The [`ElementDefinition`] whose FHIR type is to be determined.
361    ///
362    /// # Returns
363    ///
364    /// A `String` containing the FHIR type name (e.g. `"Patient"`, `"string"`,
365    /// `"CodeableConcept"`).
366    pub fn fhir_type(sd: &StructureDefinition, element: &ElementDefinition) -> String {
367        if crate::utilities::conditionals::is_root(sd, element) {
368            sd.type_
369                .value
370                .as_ref()
371                .expect("Root element must have a type")
372                .clone()
373        } else {
374            let default_types = vec![];
375            let fhir_types = element.type_.as_ref().unwrap_or(&default_types);
376            if fhir_types.len() == 1 {
377                fhir_types[0]
378                    .code
379                    .value
380                    .as_ref()
381                    .expect("Type must have a code")
382                    .clone()
383            } else {
384                panic!("Element has multiple types, cannot determine FHIR type");
385            }
386        }
387    }
388
389    #[derive(Clone, Copy)]
390    pub enum Max {
391        Unlimited,
392        Fixed(u64),
393    }
394
395    pub fn cardinality(element: &ElementDefinition) -> (u64, Max) {
396        let min = element.min.as_ref().and_then(|m| m.value).map_or(0, |m| m);
397
398        let max = element
399            .max
400            .as_ref()
401            .and_then(|m| m.value.as_ref())
402            .map(std::string::String::as_str)
403            .and_then(|s| {
404                if s == "*" {
405                    Some(Max::Unlimited)
406                } else {
407                    s.parse::<u64>().ok().map(Max::Fixed)
408                }
409            });
410
411        (min, max.unwrap_or(Max::Fixed(1)))
412    }
413}
414
415pub mod generate {
416    use std::collections::HashMap;
417
418    use haste_fhir_model::r4::generated::{
419        resources::StructureDefinition, types::ElementDefinition,
420    };
421    use proc_macro2::TokenStream;
422    use quote::{format_ident, quote};
423
424    use crate::utilities::{FHIR_PRIMITIVES, conditionals, conversion, extract};
425
426    /// Capitalize the first character in s.
427    #[must_use]
428    pub fn capitalize(s: &str) -> String {
429        let mut c = s.chars();
430        match c.next() {
431            None => String::new(),
432            Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
433        }
434    }
435
436    /// Returns the generated Rust struct name for a FHIR element.
437    ///
438    /// For the root element, the struct name is derived from the structure
439    /// definition's `id`. Primitive FHIR types are prefixed with `"FHIR"` to
440    /// distinguish them from Rust primitive types (e.g. `string` → `FHIRString`).
441    ///
442    /// For nested elements, the struct name is constructed by:
443    /// - Splitting the element id on `'.'`.
444    /// - Capitalizing each path segment.
445    /// - Concatenating the segments.
446    /// - Removing the FHIR choice-type marker (`[x]`), if present.
447    ///
448    /// # Panics
449    ///
450    /// This function will panic if:
451    /// - The root [`StructureDefinition`] does not have an `id`.
452    /// - A non-root [`ElementDefinition`] does not have an `id`.
453    ///
454    /// # Arguments
455    ///
456    /// * `sd` - The [`StructureDefinition`] containing the element.
457    /// * `element` - The [`ElementDefinition`] for which to generate a struct name.
458    ///
459    /// # Returns
460    ///
461    /// A `String` containing the generated Rust struct name.
462    pub fn struct_name(sd: &StructureDefinition, element: &ElementDefinition) -> String {
463        if conditionals::is_root(sd, element) {
464            let mut interface_name: String = capitalize(sd.id.as_ref().unwrap());
465            if conditionals::is_primitive_sd(sd) {
466                interface_name = "FHIR".to_owned() + &interface_name;
467            }
468            interface_name
469        } else {
470            element
471                .id
472                .as_ref()
473                .map(|p| p.split('.'))
474                .map(|p| p.map(capitalize).collect::<String>())
475                .unwrap()
476                .replace("[x]", "")
477        }
478    }
479
480    pub fn type_choice_name(sd: &StructureDefinition, element: &ElementDefinition) -> String {
481        let name = struct_name(sd, element);
482        name + "TypeChoice"
483    }
484
485    pub fn type_choice_variant_name(element: &ElementDefinition, fhir_type: &str) -> String {
486        let field_name = extract::field_name(&extract::path(element));
487        format!("{:0}{:1}", field_name, capitalize(fhir_type))
488    }
489
490    pub fn create_type_choice_variants(element: &ElementDefinition) -> Vec<String> {
491        extract::field_types(element)
492            .into_iter()
493            .map(|fhir_type| type_choice_variant_name(element, fhir_type))
494            .collect()
495    }
496    pub fn create_type_choice_primitive_variants(element: &ElementDefinition) -> Vec<String> {
497        extract::field_types(element)
498            .into_iter()
499            .filter(|fhir_type| FHIR_PRIMITIVES.contains_key(*fhir_type))
500            .map(|fhir_type| type_choice_variant_name(element, fhir_type))
501            .collect()
502    }
503
504    /// Returns the Rust type for a generated struct field.
505    ///
506    /// The returned type depends on the kind of FHIR element:
507    ///
508    /// - **Choice elements** (`[x]`) use the generated choice enum.
509    /// - **Nested complex elements** use the generated nested struct.
510    /// - **All other elements** are mapped from their FHIR type to the
511    ///   corresponding Rust type.
512    ///
513    /// The returned tuple contains:
514    /// - The Rust type as a [`TokenStream`].
515    /// - A boolean indicating whether the type is a primitive/value type, as
516    ///   determined by [`conversion::fhir_type_to_rust_type`].
517    ///
518    /// # Panics
519    ///
520    /// This function will panic if a non-choice, non-nested element does not have
521    /// a declared FHIR type or if the type code is missing.
522    ///
523    /// # Arguments
524    ///
525    /// * `sd` - The [`StructureDefinition`] containing the element.
526    /// * `element` - The [`ElementDefinition`] whose field type is being generated.
527    /// * `inlined_terminology` - A mapping of FHIR terminology bindings to generated
528    ///   Rust types.
529    ///
530    /// # Returns
531    ///
532    /// A tuple `(TokenStream, bool)` where:
533    /// - `TokenStream` is the generated Rust type.
534    /// - `bool` indicates whether the type is a primitive/value type.
535    pub fn field_typename<S: ::std::hash::BuildHasher>(
536        sd: &StructureDefinition,
537        element: &ElementDefinition,
538        inlined_terminology: &HashMap<String, String, S>,
539    ) -> (TokenStream, bool) {
540        if conditionals::is_typechoice(element) {
541            let k = format_ident!("{}", type_choice_name(sd, element));
542            (
543                quote! {
544                    #k
545                },
546                false,
547            )
548        } else if conditionals::is_nested_complex(element) {
549            let k = format_ident!("{}", struct_name(sd, element));
550            (
551                quote! {
552                    #k
553                },
554                false,
555            )
556        } else {
557            let fhir_type = element.type_.as_ref().unwrap()[0]
558                .code
559                .as_ref()
560                .value
561                .as_ref()
562                .unwrap();
563
564            conversion::fhir_type_to_rust_type(element, fhir_type, inlined_terminology)
565        }
566    }
567}
568
569pub mod conditionals {
570    use haste_fhir_model::r4::generated::{
571        resources::StructureDefinition, terminology::StructureDefinitionKind,
572        types::ElementDefinition,
573    };
574
575    use crate::utilities::{FHIR_PRIMITIVES, RUST_PRIMITIVES, extract};
576
577    pub fn is_root(sd: &StructureDefinition, element: &ElementDefinition) -> bool {
578        element.path.value == sd.id
579    }
580
581    pub fn is_resource_sd(sd: &StructureDefinition) -> bool {
582        sd.kind == StructureDefinitionKind::resource()
583    }
584
585    pub fn is_primitive_type(fhir_type: &str) -> bool {
586        FHIR_PRIMITIVES.contains_key(fhir_type)
587    }
588
589    pub fn is_primitive_element(element: &ElementDefinition) -> bool {
590        let types = extract::field_types(element);
591        types.len() == 1 && is_primitive_type(types[0])
592    }
593
594    pub fn is_nested_complex(element: &ElementDefinition) -> bool {
595        let types = extract::field_types(element);
596        // Backbone or Typechoice elements Have inlined types created.
597        types.len() > 1 || types[0] == "BackboneElement" || types[0] == "Element"
598    }
599
600    // All structs should be boxed if they are not rust primitive types.
601    pub fn should_be_boxed(fhir_type: &str) -> bool {
602        !RUST_PRIMITIVES.contains_key(fhir_type)
603    }
604
605    pub fn is_primitive_sd(sd: &StructureDefinition) -> bool {
606        sd.kind == StructureDefinitionKind::primitive_type()
607    }
608
609    pub fn is_typechoice(element: &ElementDefinition) -> bool {
610        extract::field_types(element).len() > 1
611    }
612}
613
614pub mod load {
615    use std::path::Path;
616
617    use haste_fhir_model::r4::generated::{
618        resources::{Resource, StructureDefinition},
619        terminology::StructureDefinitionKind,
620    };
621
622    use crate::utilities::extract;
623
624    /// Loads a FHIR resource from a JSON file.
625    ///
626    /// The file is read as UTF-8 text and deserialized into a [`Resource`] using
627    /// `serde_json`.
628    ///
629    /// # Arguments
630    ///
631    /// * `file_path` - The path to the JSON file containing the FHIR resource.
632    ///
633    /// # Errors
634    ///
635    /// Returns an error if:
636    ///
637    /// * The file cannot be read from the provided path.
638    /// * The file contents cannot be parsed as valid JSON.
639    /// * The parsed JSON does not match the expected [`Resource`] structure.
640    ///
641    /// The returned error message includes the underlying cause of the failure.
642    pub fn load_from_file(file_path: &Path) -> Result<Resource, String> {
643        let data =
644            std::fs::read_to_string(file_path).map_err(|e| format!("Failed to read file: {e}"))?;
645
646        let resource = serde_json::from_str::<Resource>(&data)
647            .map_err(|e| format!("Failed to parse JSON: {e}"))?;
648
649        Ok(resource)
650    }
651
652    /// Retrieves [`StructureDefinition`] resources from a FHIR [`Resource`].
653    ///
654    /// This function supports extracting structure definitions from:
655    ///
656    /// - A [`Resource::Bundle`], by collecting all entries containing a
657    ///   [`Resource::StructureDefinition`].
658    /// - A standalone [`Resource::StructureDefinition`].
659    ///
660    /// The returned definitions can optionally be filtered by their kind using the
661    /// `level` parameter:
662    ///
663    /// - `"resource"` - Includes resource definitions.
664    /// - `"complex-type"` - Includes complex type definitions.
665    /// - `"primitive-type"` - Includes primitive type definitions.
666    ///
667    /// If no level filter is provided, all matching [`StructureDefinition`] values
668    /// are returned.
669    ///
670    /// # Arguments
671    ///
672    /// * `resource` - The FHIR resource containing one or more structure
673    ///   definitions.
674    /// * `level` - Optional filter specifying the structure definition category to
675    ///   return.
676    ///
677    /// # Returns
678    ///
679    /// Returns a vector of references to matching [`StructureDefinition`] values.
680    ///
681    /// # Errors
682    ///
683    /// This function currently does not return any errors during execution and
684    /// returns `Ok` in all cases. The `Result` return type is reserved for future
685    /// error handling.
686    ///
687    /// # Lifetimes
688    ///
689    /// The returned references borrow from the provided `resource` and are valid
690    /// for the same lifetime as the input resource.
691    pub fn get_structure_definitions<'a>(
692        resource: &'a Resource,
693        level: Option<&'static str>,
694    ) -> Result<Vec<&'a StructureDefinition>, String> {
695        match resource {
696            Resource::Bundle(bundle) => {
697                if let Some(entries) = bundle.entry.as_ref() {
698                    let sds = entries
699                        .iter()
700                        .filter_map(|e| e.resource.as_ref())
701                        .filter_map(|sd| match sd.as_ref() {
702                            Resource::StructureDefinition(sd) => Some(sd),
703                            _ => None,
704                        });
705
706                    let filtered_sds = sds.filter(move |sd| {
707                        if let Some(level) = level {
708                            match &sd.kind {
709                                kind if kind == &StructureDefinitionKind::resource()
710                                    || kind == &StructureDefinitionKind::null() =>
711                                {
712                                    level == "resource"
713                                }
714                                kind if kind == &StructureDefinitionKind::complex_type() => {
715                                    level == "complex-type"
716                                }
717                                kind if kind == &StructureDefinitionKind::primitive_type() => {
718                                    level == "primitive-type"
719                                }
720                                _ => false,
721                            }
722                        } else {
723                            true
724                        }
725                    });
726
727                    Ok(filtered_sds.collect())
728                } else {
729                    Ok(vec![])
730                }
731            }
732            Resource::StructureDefinition(sd) => {
733                let resources = std::iter::once(sd);
734                let filtered_resources = resources.filter(|sd| {
735                    if let Some(level) = level {
736                        match &sd.kind {
737                            kind if kind == &StructureDefinitionKind::resource()
738                                || kind == &StructureDefinitionKind::null() =>
739                            {
740                                level == "resource"
741                            }
742                            kind if kind == &StructureDefinitionKind::complex_type() => {
743                                level == "complex-type"
744                            }
745                            kind if kind == &StructureDefinitionKind::primitive_type() => {
746                                level == "primitive-type"
747                            }
748                            _ => false,
749                        }
750                    } else {
751                        true
752                    }
753                });
754
755                Ok(filtered_resources.collect())
756            }
757            _ => Ok(vec![]),
758        }
759    }
760}