openquant/util/input_error.rs
1//! The error returned by functions whose only failure mode is an invalid argument.
2//!
3//! [`InputError`] is shared by modules that validate their arguments but have no richer error
4//! type of their own (for example [`crate::util::fast_ewma`], [`crate::sampling`],
5//! [`crate::ef3m`] and [`crate::backtest_statistics`]). Each variant names the offending
6//! argument, so the [`Display`](std::fmt::Display) message can be shown to a user as is.
7//!
8//! ```
9//! use openquant::util::fast_ewma::ewma;
10//! use openquant::util::InputError;
11//!
12//! let err = ewma(&[1.0, 2.0], 0).unwrap_err();
13//! assert_eq!(
14//! err,
15//! InputError::OutOfRange { name: "window", value: 0.0, expected: "a positive integer" }
16//! );
17//! assert_eq!(err.to_string(), "'window' is 0, expected a positive integer");
18//! ```
19
20use std::fmt;
21
22/// An argument was rejected before any computation ran.
23///
24/// `name` is the argument (or, for rows of a matrix, a short description of it) as it appears
25/// in the function signature.
26#[derive(Debug, Clone, PartialEq)]
27pub enum InputError {
28 /// `name` must contain at least `min` values.
29 TooShort {
30 /// The argument that was too short.
31 name: &'static str,
32 /// Its actual length.
33 len: usize,
34 /// The minimum length the function needs.
35 min: usize,
36 },
37 /// `name` must be as long as the series it is paired with.
38 LengthMismatch {
39 /// The argument whose length is wrong.
40 name: &'static str,
41 /// Its actual length.
42 len: usize,
43 /// The length it must have to match its partner.
44 expected: usize,
45 },
46 /// `name` holds a value outside the range the function is defined on.
47 OutOfRange {
48 /// The argument that is out of range.
49 name: &'static str,
50 /// The offending value (converted to `f64` for integer arguments).
51 value: f64,
52 /// A human-readable description of the accepted range.
53 expected: &'static str,
54 },
55}
56
57impl fmt::Display for InputError {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match self {
60 InputError::TooShort { name, len, min } => {
61 write!(f, "'{name}' has length {len}, need at least {min}")
62 }
63 InputError::LengthMismatch { name, len, expected } => {
64 write!(f, "'{name}' has length {len}, expected {expected}")
65 }
66 InputError::OutOfRange { name, value, expected } => {
67 write!(f, "'{name}' is {value}, expected {expected}")
68 }
69 }
70 }
71}
72
73impl std::error::Error for InputError {}
74
75pub(crate) fn same_length(
76 name: &'static str,
77 values: &[f64],
78 expected: usize,
79) -> Result<(), InputError> {
80 if values.len() == expected {
81 Ok(())
82 } else {
83 Err(InputError::LengthMismatch { name, len: values.len(), expected })
84 }
85}