haste_fhir_operation_error/
lib.rs

1use std::{error::Error, fmt::Display};
2
3use haste_fhir_model::r4::generated::{
4    resources::{OperationOutcome, OperationOutcomeIssue},
5    terminology::{IssueSeverity, IssueType},
6    types::FHIRString,
7};
8
9#[cfg(feature = "derive")]
10pub mod derive;
11
12#[cfg(feature = "axum")]
13pub mod axum;
14
15#[derive(Debug)]
16pub struct OperationOutcomeError {
17    _source: Option<anyhow::Error>,
18    outcome: OperationOutcome,
19}
20
21fn create_operation_outcome(
22    severity: IssueSeverity,
23    code: IssueType,
24    diagnostic: String,
25) -> OperationOutcome {
26    OperationOutcome {
27        issue: vec![OperationOutcomeIssue {
28            severity: Box::new(severity),
29            code: Box::new(code),
30            diagnostics: Some(Box::new(FHIRString {
31                value: Some(diagnostic),
32                ..Default::default()
33            })),
34            ..Default::default()
35        }],
36        ..Default::default()
37    }
38}
39
40impl OperationOutcomeError {
41    pub fn new(source: Option<anyhow::Error>, outcome: OperationOutcome) -> Self {
42        OperationOutcomeError {
43            _source: source,
44            outcome,
45        }
46    }
47
48    pub fn outcome(&self) -> &OperationOutcome {
49        &self.outcome
50    }
51
52    pub fn push_issue(
53        &mut self,
54        issue: haste_fhir_model::r4::generated::resources::OperationOutcomeIssue,
55    ) {
56        self.outcome.issue.push(issue);
57    }
58
59    pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> {
60        self._source.as_ref().map(|s| s.backtrace())
61    }
62
63    pub fn fatal(code: IssueType, diagnostic: String) -> Self {
64        OperationOutcomeError::new(
65            None,
66            create_operation_outcome(IssueSeverity::Fatal(None), code, diagnostic),
67        )
68    }
69    pub fn error(code: IssueType, diagnostic: String) -> Self {
70        OperationOutcomeError::new(
71            None,
72            create_operation_outcome(IssueSeverity::Error(None), code, diagnostic),
73        )
74    }
75    pub fn warning(code: IssueType, diagnostic: String) -> Self {
76        OperationOutcomeError::new(
77            None,
78            create_operation_outcome(IssueSeverity::Warning(None), code, diagnostic),
79        )
80    }
81    pub fn information(code: IssueType, diagnostic: String) -> Self {
82        OperationOutcomeError::new(
83            None,
84            create_operation_outcome(IssueSeverity::Information(None), code, diagnostic),
85        )
86    }
87}
88
89fn get_issue_diagnostics<'a>(
90    issue: &'a haste_fhir_model::r4::generated::resources::OperationOutcomeIssue,
91) -> Option<&'a str> {
92    issue
93        .diagnostics
94        .as_ref()
95        .and_then(|d| d.value.as_ref().map(|v| v.as_str()))
96}
97
98impl Display for OperationOutcomeError {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        write!(
101            f,
102            "Operation Error: '{}'",
103            self.outcome
104                .issue
105                .iter()
106                .map(get_issue_diagnostics)
107                .filter_map(|d| d)
108                .collect::<Vec<_>>()
109                .join(", ")
110        )
111    }
112}
113
114impl Error for OperationOutcomeError {
115    fn source(&self) -> Option<&(dyn Error + 'static)> {
116        if let Some(source) = self._source.as_ref() {
117            return Some(&**source);
118        } else {
119            None
120        }
121    }
122
123    fn description(&self) -> &str {
124        self.outcome.issue.first().map_or("Unknown error", |issue| {
125            if let Some(diagnostics) = &issue.diagnostics {
126                diagnostics
127                    .value
128                    .as_ref()
129                    .map(|v| v.as_str())
130                    .unwrap_or("No diagnostics available")
131            } else {
132                "No diagnostics available"
133            }
134        })
135    }
136
137    fn cause(&self) -> Option<&dyn Error> {
138        self.source()
139    }
140}