Skip to main content

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}