bloom_lib/error.rs
1//! The error type shared by every structure in the crate.
2
3use core::fmt;
4
5/// Errors returned by fallible operations across the crate.
6///
7/// Every constructor that accepts tuning parameters validates them and returns
8/// [`Error::InvalidParameter`] rather than panicking. Operations that combine
9/// two structures (for example [`BloomFilter::merge`](crate::BloomFilter::merge))
10/// return [`Error::IncompatibleParameters`] when the operands were not built
11/// with the same dimensions. A Cuckoo filter returns [`Error::CapacityExceeded`]
12/// when an insertion cannot find a free slot after exhausting its relocation
13/// budget.
14///
15/// The enum is `#[non_exhaustive]`: matching on it must include a wildcard arm
16/// so that future variants do not break downstream code.
17///
18/// # Examples
19///
20/// ```
21/// use bloom_lib::Error;
22///
23/// let err = Error::InvalidParameter {
24/// param: "rate",
25/// reason: "must be in (0.0, 1.0)",
26/// };
27/// assert_eq!(err.to_string(), "invalid parameter `rate`: must be in (0.0, 1.0)");
28/// ```
29#[non_exhaustive]
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum Error {
32 /// A tuning parameter fell outside its valid range.
33 ///
34 /// `param` names the offending argument; `reason` states the constraint it
35 /// violated. Both are static strings so the variant stays allocation-free
36 /// and usable on `no_std` targets.
37 InvalidParameter {
38 /// The name of the parameter that was rejected.
39 param: &'static str,
40 /// A human-readable statement of the constraint that was violated.
41 reason: &'static str,
42 },
43
44 /// Two structures could not be combined because their parameters differ.
45 ///
46 /// Merging, intersecting, or comparing two probabilistic structures only
47 /// has a defined meaning when both were constructed with identical
48 /// dimensions (bit count, hash count, register precision, and so on).
49 IncompatibleParameters,
50
51 /// An insertion failed because the structure is full.
52 ///
53 /// Returned by [`CuckooFilter::insert`](crate::CuckooFilter::insert) when no
54 /// free slot is found within the relocation budget. It signals that the
55 /// filter has reached its practical load limit; create a larger filter.
56 CapacityExceeded,
57}
58
59impl fmt::Display for Error {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 Error::InvalidParameter { param, reason } => {
63 write!(f, "invalid parameter `{param}`: {reason}")
64 }
65 Error::IncompatibleParameters => {
66 f.write_str("structures were built with incompatible parameters")
67 }
68 Error::CapacityExceeded => {
69 f.write_str("structure is full: insertion exceeded its capacity")
70 }
71 }
72 }
73}
74
75#[cfg(feature = "std")]
76impl std::error::Error for Error {}
77
78#[cfg(all(test, feature = "std"))]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_display_invalid_parameter() {
84 let err = Error::InvalidParameter {
85 param: "rate",
86 reason: "must be in (0.0, 1.0)",
87 };
88 assert_eq!(
89 err.to_string(),
90 "invalid parameter `rate`: must be in (0.0, 1.0)"
91 );
92 }
93
94 #[test]
95 fn test_display_incompatible_parameters() {
96 assert_eq!(
97 Error::IncompatibleParameters.to_string(),
98 "structures were built with incompatible parameters"
99 );
100 }
101
102 #[test]
103 fn test_display_capacity_exceeded() {
104 assert_eq!(
105 Error::CapacityExceeded.to_string(),
106 "structure is full: insertion exceeded its capacity"
107 );
108 }
109
110 #[test]
111 fn test_is_std_error() {
112 // Confirms the `std::error::Error` impl is wired up.
113 fn assert_error<E: std::error::Error>(_: &E) {}
114 assert_error(&Error::CapacityExceeded);
115 }
116
117 #[test]
118 fn test_clone_and_eq() {
119 let err = Error::InvalidParameter {
120 param: "k",
121 reason: "must be greater than zero",
122 };
123 assert_eq!(err.clone(), err);
124 assert_ne!(err, Error::CapacityExceeded);
125 }
126}