Skip to main content

rok_utils/
result.rs

1//! Result / error extension traits.
2
3use std::fmt::Display;
4
5/// Extension methods on `Option<T>`.
6pub trait OptionExt<T> {
7    /// Convert `None` into an [`anyhow::Error`] with the given message.
8    fn context(self, msg: impl Display) -> anyhow::Result<T>;
9
10    /// Like [`OptionExt::context`] but the message is lazily constructed.
11    fn with_context<F, M>(self, f: F) -> anyhow::Result<T>
12    where
13        F: FnOnce() -> M,
14        M: Display;
15}
16
17impl<T> OptionExt<T> for Option<T> {
18    fn context(self, msg: impl Display) -> anyhow::Result<T> {
19        self.ok_or_else(|| anyhow::anyhow!("{}", msg))
20    }
21
22    fn with_context<F, M>(self, f: F) -> anyhow::Result<T>
23    where
24        F: FnOnce() -> M,
25        M: Display,
26    {
27        self.ok_or_else(|| anyhow::anyhow!("{}", f()))
28    }
29}
30
31// ── tests ────────────────────────────────────────────────────────────────────
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn none_becomes_error() {
39        let r: anyhow::Result<i32> = None::<i32>.context("missing value");
40        assert!(r.is_err());
41        assert_eq!(r.unwrap_err().to_string(), "missing value");
42    }
43
44    #[test]
45    fn some_passes_through() {
46        let r: anyhow::Result<i32> = Some(42).context("unreachable");
47        assert_eq!(r.unwrap(), 42);
48    }
49}