Skip to main content

qsm_core/
error.rs

1//! The error an algorithm returns when it is handed input it cannot process.
2
3/// Input an algorithm cannot process: too few echoes, echo times it cannot use, buffers that do
4/// not match the grid, a singular affine, and so on. The message says what was wrong and with
5/// what values, so a host can pass it straight to its user.
6///
7/// Algorithms return this instead of panicking. A host that wants to reject a run before any
8/// work starts should still check its parameters itself (the message here is the last line of
9/// defence, not a substitute for validating a config).
10///
11/// The pipeline stages convert it to [`PipelineError::InvalidInput`](crate::pipeline::PipelineError::InvalidInput).
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct InputError(pub String);
14
15impl InputError {
16    /// An error with this message.
17    pub fn new(msg: impl Into<String>) -> Self {
18        Self(msg.into())
19    }
20
21    /// A parameter outside the range the algorithm accepts, as
22    /// `"{name} = {value} is out of range: must be {allowed}"`. `name` should say whose
23    /// parameter it is (`"V-SHARP min_radius"`), and `allowed` the range with its unit
24    /// (`"finite and > 0 mm"`), so the message stands on its own in a host's error report.
25    pub fn param(name: &str, value: impl std::fmt::Display, allowed: &str) -> Self {
26        Self(format!("{name} = {value} is out of range: must be {allowed}"))
27    }
28}
29
30impl std::fmt::Display for InputError {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.write_str(&self.0)
33    }
34}
35
36impl std::error::Error for InputError {}
37
38/// `Err(InputError)` unless `len == expected`, naming the buffer.
39pub(crate) fn check_len(name: &str, len: usize, expected: usize) -> Result<(), InputError> {
40    if len == expected {
41        Ok(())
42    } else {
43        Err(InputError(format!("{name} has {len} values, expected {expected}")))
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn param_error_names_parameter_value_and_range() {
53        let e = InputError::param("V-SHARP min_radius", 0.0, "finite and > 0 mm");
54        assert_eq!(e.to_string(), "V-SHARP min_radius = 0 is out of range: must be finite and > 0 mm");
55    }
56}