haste_fhir_operation_error/
lib.rs1use 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}