haste_repository/
utilities.rs

1use haste_fhir_model::r4::generated::{
2    resources::Resource,
3    terminology::IssueType,
4    types::{FHIRId, Meta},
5};
6use haste_fhir_operation_error::{OperationOutcomeError, derive::OperationOutcomeError};
7use haste_reflect::MetaValue;
8
9static ID_CHARACTERS: &[char] = &[
10    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
11    'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '-',
12];
13
14// [A-Za-z0-9\-\.]{1,64} See https://hl7.org/fhir/r4/datatypes.html#id
15// Can't use _ for compliance.
16pub fn generate_id(len: Option<usize>) -> String {
17    let len = len.unwrap_or(26);
18    nanoid::nanoid!(len, ID_CHARACTERS).to_string()
19}
20
21pub fn validate_id(id: &str) -> Result<(), OperationOutcomeError> {
22    let characters_allowed = ID_CHARACTERS.iter().collect::<String>();
23    let re = regex::Regex::new(&format!("^[{}]*$", characters_allowed)).unwrap();
24    if !re.is_match(id) {
25        Err(OperationOutcomeError::fatal(
26            IssueType::Invalid(None),
27            format!("ID contains invalid characters: {}", id),
28        ))
29    } else {
30        Ok(())
31    }
32}
33
34#[derive(OperationOutcomeError)]
35pub enum DataTransformError {
36    #[error(code = "invalid", diagnostic = "Invalid data: '{arg0}'")]
37    InvalidData(String),
38    #[error(code = "not-found", diagnostic = "Data not found")]
39    NotFound(String),
40}
41
42pub fn set_resource_id(
43    resource: &mut Resource,
44    id_: Option<String>,
45) -> Result<(), OperationOutcomeError> {
46    let id: &mut dyn std::any::Any =
47        resource
48            .get_field_mut("id")
49            .ok_or(DataTransformError::InvalidData(
50                "Missing 'id' field".to_string(),
51            ))?;
52    let id: &mut Option<String> =
53        id.downcast_mut::<Option<String>>()
54            .ok_or(DataTransformError::InvalidData(
55                "Invalid 'id' field".to_string(),
56            ))?;
57    *id = Some(id_.unwrap_or_else(|| generate_id(None)));
58    Ok(())
59}
60
61pub fn set_version_id(resource: &mut Resource) -> Result<(), OperationOutcomeError> {
62    let meta: &mut dyn std::any::Any =
63        resource
64            .get_field_mut("meta")
65            .ok_or(DataTransformError::InvalidData(
66                "Missing 'meta' field".to_string(),
67            ))?;
68    let meta: &mut Option<Box<Meta>> =
69        meta.downcast_mut::<Option<Box<Meta>>>()
70            .ok_or(DataTransformError::InvalidData(
71                "Invalid 'meta' field".to_string(),
72            ))?;
73
74    if meta.is_none() {
75        *meta = Some(Box::new(Meta::default()))
76    }
77    meta.as_mut().map(|meta| {
78        meta.versionId = Some(Box::new(FHIRId {
79            id: None,
80            extension: None,
81            value: Some(generate_id(None)),
82        }));
83    });
84
85    Ok(())
86}