haste_repository/
utilities.rs1use std::sync::LazyLock;
2
3use haste_fhir_model::r4::generated::{
4 resources::Resource,
5 terminology::IssueType,
6 types::{FHIRId, Meta},
7};
8use haste_fhir_operation_error::{OperationOutcomeError, derive::OperationOutcomeError};
9use haste_reflect::MetaValue;
10
11static ID_CHARACTERS: &[char] = &[
12 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
13 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '-',
14];
15
16pub fn generate_id(len: Option<usize>) -> String {
19 let len = len.unwrap_or(26);
20 nanoid::nanoid!(len, ID_CHARACTERS).to_string()
21}
22
23static ID_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {
24 let characters_allowed = ID_CHARACTERS.iter().collect::<String>();
25 regex::Regex::new(&format!("^[{characters_allowed}]*$"))
26 .expect("ID_CHARACTERS should produce a valid regex")
27});
28
29pub fn validate_id(id: &str) -> Result<(), OperationOutcomeError> {
36 if ID_REGEX.is_match(id) {
37 Ok(())
38 } else {
39 Err(OperationOutcomeError::fatal(
40 IssueType::invalid(),
41 format!("ID contains invalid characters: {id}"),
42 ))
43 }
44}
45
46#[derive(OperationOutcomeError)]
47pub enum DataTransformError {
48 #[error(code = "invalid", diagnostic = "Invalid data: '{arg0}'")]
49 InvalidData(String),
50 #[error(code = "not-found", diagnostic = "Data not found")]
51 NotFound(String),
52}
53
54pub fn set_resource_id(
64 resource: &mut Resource,
65 id_: Option<String>,
66) -> Result<(), OperationOutcomeError> {
67 let id: &mut dyn std::any::Any =
68 resource
69 .get_field_mut("id")
70 .ok_or(DataTransformError::InvalidData(
71 "Missing 'id' field".to_string(),
72 ))?;
73 let id: &mut Option<String> =
74 id.downcast_mut::<Option<String>>()
75 .ok_or(DataTransformError::InvalidData(
76 "Invalid 'id' field".to_string(),
77 ))?;
78 *id = Some(id_.unwrap_or_else(|| generate_id(None)));
79 Ok(())
80}
81
82pub fn set_version_id(resource: &mut Resource) -> Result<(), OperationOutcomeError> {
92 let meta: &mut dyn std::any::Any =
93 resource
94 .get_field_mut("meta")
95 .ok_or(DataTransformError::InvalidData(
96 "Missing 'meta' field".to_string(),
97 ))?;
98 let meta: &mut Option<Box<Meta>> =
99 meta.downcast_mut::<Option<Box<Meta>>>()
100 .ok_or(DataTransformError::InvalidData(
101 "Invalid 'meta' field".to_string(),
102 ))?;
103
104 if meta.is_none() {
105 *meta = Some(Box::new(Meta::default()));
106 }
107 if let Some(meta) = meta.as_mut() {
108 meta.versionId = Some(Box::new(FHIRId {
109 id: None,
110 extension: None,
111 value: Some(generate_id(None)),
112 }));
113 }
114
115 Ok(())
116}