Skip to main content

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::{BoundCode, 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: Box<OperationOutcome>,
19}
20
21fn create_operation_outcome(
22    severity: BoundCode<IssueSeverity>,
23    code: BoundCode<IssueType>,
24    diagnostic: String,
25) -> OperationOutcome {
26    OperationOutcome {
27        issue: vec![OperationOutcomeIssue {
28            severity,
29            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    #[must_use]
42    pub fn new(source: Option<anyhow::Error>, outcome: OperationOutcome) -> Self {
43        OperationOutcomeError {
44            source,
45            outcome: Box::new(outcome),
46        }
47    }
48
49    #[must_use]
50    pub fn outcome(&self) -> &OperationOutcome {
51        &self.outcome
52    }
53
54    pub fn push_issue(
55        &mut self,
56        issue: haste_fhir_model::r4::generated::resources::OperationOutcomeIssue,
57    ) {
58        self.outcome.issue.push(issue);
59    }
60
61    #[must_use]
62    pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> {
63        self.source.as_ref().map(anyhow::Error::backtrace)
64    }
65
66    #[must_use]
67    pub fn fatal(code: BoundCode<IssueType>, diagnostic: String) -> Self {
68        OperationOutcomeError::new(
69            None,
70            create_operation_outcome(IssueSeverity::fatal(), code, diagnostic),
71        )
72    }
73    #[must_use]
74    pub fn error(code: BoundCode<IssueType>, diagnostic: String) -> Self {
75        OperationOutcomeError::new(
76            None,
77            create_operation_outcome(IssueSeverity::error(), code, diagnostic),
78        )
79    }
80    #[must_use]
81    pub fn warning(code: BoundCode<IssueType>, diagnostic: String) -> Self {
82        OperationOutcomeError::new(
83            None,
84            create_operation_outcome(IssueSeverity::warning(), code, diagnostic),
85        )
86    }
87    #[must_use]
88    pub fn information(code: BoundCode<IssueType>, diagnostic: String) -> Self {
89        OperationOutcomeError::new(
90            None,
91            create_operation_outcome(IssueSeverity::information(), code, diagnostic),
92        )
93    }
94}
95
96fn get_issue_diagnostics(
97    issue: &haste_fhir_model::r4::generated::resources::OperationOutcomeIssue,
98) -> Option<&str> {
99    issue.diagnostics.as_ref().and_then(|d| d.value.as_deref())
100}
101
102impl Display for OperationOutcomeError {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        write!(
105            f,
106            "Operation Error: '{}'",
107            self.outcome
108                .issue
109                .iter()
110                .filter_map(get_issue_diagnostics)
111                .collect::<Vec<_>>()
112                .join(", ")
113        )
114    }
115}
116
117impl Error for OperationOutcomeError {
118    fn source(&self) -> Option<&(dyn Error + 'static)> {
119        if let Some(source) = self.source.as_ref() {
120            Some(&**source)
121        } else {
122            None
123        }
124    }
125
126    fn description(&self) -> &str {
127        self.outcome.issue.first().map_or("Unknown error", |issue| {
128            if let Some(diagnostics) = &issue.diagnostics {
129                diagnostics
130                    .value
131                    .as_deref()
132                    .map_or("No diagnostics available", |v| v)
133            } else {
134                "No diagnostics available"
135            }
136        })
137    }
138
139    fn cause(&self) -> Option<&dyn Error> {
140        self.source()
141    }
142}