Skip to main content

helios_persistence/search/
errors.rs

1//! Search-specific error types.
2//!
3//! `LoaderError` and `RegistryError` moved to
4//! [`helios_fhir::search::errors`] alongside the registry. `ExtractionError`
5//! and `ReindexError` stay here — they're index-feed concerns.
6
7use std::fmt;
8
9use serde::{Deserialize, Serialize};
10
11pub use helios_fhir::search::errors::{LoaderError, RegistryError};
12
13/// Error during value extraction.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub enum ExtractionError {
16    /// FHIRPath evaluation failed.
17    EvaluationFailed {
18        /// The parameter name.
19        param_name: String,
20        /// The FHIRPath expression.
21        expression: String,
22        /// Error message.
23        error: String,
24    },
25
26    /// Value conversion failed.
27    ConversionFailed {
28        /// The parameter name.
29        param_name: String,
30        /// The expected type.
31        expected_type: String,
32        /// What was actually found.
33        actual_value: String,
34    },
35
36    /// Unsupported value type for indexing.
37    UnsupportedType {
38        /// The parameter name.
39        param_name: String,
40        /// The unsupported type.
41        value_type: String,
42    },
43
44    /// Resource is not a valid JSON object.
45    InvalidResource {
46        /// Description of the problem.
47        message: String,
48    },
49
50    /// FHIRPath expression evaluation error.
51    FhirPathError {
52        /// The FHIRPath expression that failed.
53        expression: String,
54        /// The error message from the evaluator.
55        message: String,
56    },
57
58    /// Generic value conversion error.
59    ConversionError {
60        /// Description of the conversion error.
61        message: String,
62    },
63}
64
65impl fmt::Display for ExtractionError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            ExtractionError::EvaluationFailed {
69                param_name,
70                expression,
71                error,
72            } => {
73                write!(
74                    f,
75                    "Failed to evaluate '{}' for parameter '{}': {}",
76                    expression, param_name, error
77                )
78            }
79            ExtractionError::ConversionFailed {
80                param_name,
81                expected_type,
82                actual_value,
83            } => {
84                write!(
85                    f,
86                    "Cannot convert '{}' to {} for parameter '{}'",
87                    actual_value, expected_type, param_name
88                )
89            }
90            ExtractionError::UnsupportedType {
91                param_name,
92                value_type,
93            } => {
94                write!(
95                    f,
96                    "Unsupported value type '{}' for parameter '{}'",
97                    value_type, param_name
98                )
99            }
100            ExtractionError::InvalidResource { message } => {
101                write!(f, "Invalid resource: {}", message)
102            }
103            ExtractionError::FhirPathError {
104                expression,
105                message,
106            } => {
107                write!(f, "FHIRPath error evaluating '{}': {}", expression, message)
108            }
109            ExtractionError::ConversionError { message } => {
110                write!(f, "Conversion error: {}", message)
111            }
112        }
113    }
114}
115
116impl std::error::Error for ExtractionError {}
117
118/// Error during reindex operations.
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub enum ReindexError {
121    /// Reindex job not found.
122    JobNotFound {
123        /// The job ID.
124        job_id: String,
125    },
126
127    /// Reindex job already running.
128    AlreadyRunning {
129        /// The existing job ID.
130        existing_job_id: String,
131    },
132
133    /// Failed to process resource during reindex.
134    ProcessingFailed {
135        /// Resource type.
136        resource_type: String,
137        /// Resource ID.
138        resource_id: String,
139        /// Error message.
140        error: String,
141    },
142
143    /// Storage error during reindex.
144    StorageError {
145        /// Error message.
146        message: String,
147    },
148
149    /// Reindex was cancelled.
150    Cancelled {
151        /// Job ID that was cancelled.
152        job_id: String,
153    },
154}
155
156impl fmt::Display for ReindexError {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        match self {
159            ReindexError::JobNotFound { job_id } => {
160                write!(f, "Reindex job '{}' not found", job_id)
161            }
162            ReindexError::AlreadyRunning { existing_job_id } => {
163                write!(
164                    f,
165                    "Reindex already running with job ID '{}'",
166                    existing_job_id
167                )
168            }
169            ReindexError::ProcessingFailed {
170                resource_type,
171                resource_id,
172                error,
173            } => {
174                write!(
175                    f,
176                    "Failed to reindex {}/{}: {}",
177                    resource_type, resource_id, error
178                )
179            }
180            ReindexError::StorageError { message } => {
181                write!(f, "Storage error during reindex: {}", message)
182            }
183            ReindexError::Cancelled { job_id } => {
184                write!(f, "Reindex job '{}' was cancelled", job_id)
185            }
186        }
187    }
188}
189
190impl std::error::Error for ReindexError {}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn test_extraction_error_display() {
198        let err = ExtractionError::EvaluationFailed {
199            param_name: "name".to_string(),
200            expression: "Patient.name".to_string(),
201            error: "syntax error".to_string(),
202        };
203        assert!(err.to_string().contains("name"));
204        assert!(err.to_string().contains("Patient.name"));
205    }
206
207    #[test]
208    fn test_reindex_error_display() {
209        let err = ReindexError::ProcessingFailed {
210            resource_type: "Patient".to_string(),
211            resource_id: "123".to_string(),
212            error: "database error".to_string(),
213        };
214        assert!(err.to_string().contains("Patient/123"));
215    }
216}