compio_buf/
buf_result.rs

1use std::io;
2#[cfg(feature = "try_trait_v2")]
3use std::{
4    convert::Infallible,
5    ops::{ControlFlow, FromResidual, Residual, Try},
6};
7
8use crate::IntoInner;
9
10/// A specialized `Result` type for operations with buffers.
11///
12/// This type is used as a return value for asynchronous compio methods that
13/// require passing ownership of a buffer to the runtime. When the operation
14/// completes, the buffer is returned no matter if the operation completed
15/// successfully.
16#[must_use]
17pub struct BufResult<T, B>(pub io::Result<T>, pub B);
18
19impl<T, B> BufResult<T, B> {
20    /// Returns [`true`] if the result is [`Ok`].
21    pub const fn is_ok(&self) -> bool {
22        self.0.is_ok()
23    }
24
25    /// Returns [`true`] if the result is [`Err`].
26    pub const fn is_err(&self) -> bool {
27        self.0.is_err()
28    }
29
30    /// Maps the result part, and allows updating the buffer.
31    #[inline]
32    pub fn map<U>(self, f: impl FnOnce(T, B) -> (U, B)) -> BufResult<U, B> {
33        match self.0 {
34            Ok(res) => {
35                let (res, buf) = f(res, self.1);
36                BufResult(Ok(res), buf)
37            }
38            Err(e) => BufResult(Err(e), self.1),
39        }
40    }
41
42    /// Maps the result part, and allows changing the buffer type.
43    #[inline]
44    pub fn map2<U, C>(
45        self,
46        f_ok: impl FnOnce(T, B) -> (U, C),
47        f_err: impl FnOnce(B) -> C,
48    ) -> BufResult<U, C> {
49        match self.0 {
50            Ok(res) => {
51                let (res, buf) = f_ok(res, self.1);
52                BufResult(Ok(res), buf)
53            }
54            Err(e) => BufResult(Err(e), f_err(self.1)),
55        }
56    }
57
58    /// Maps the result part, and keeps the buffer unchanged.
59    #[inline]
60    pub fn map_res<U>(self, f: impl FnOnce(T) -> U) -> BufResult<U, B> {
61        BufResult(self.0.map(f), self.1)
62    }
63
64    /// Maps the buffer part, and keeps the result unchanged.
65    #[inline]
66    pub fn map_buffer<C>(self, f: impl FnOnce(B) -> C) -> BufResult<T, C> {
67        BufResult(self.0, f(self.1))
68    }
69
70    /// Updating the result type and modifying the buffer.
71    #[inline]
72    pub fn and_then<U>(self, f: impl FnOnce(T, B) -> (io::Result<U>, B)) -> BufResult<U, B> {
73        match self.0 {
74            Ok(res) => BufResult::from(f(res, self.1)),
75            Err(e) => BufResult(Err(e), self.1),
76        }
77    }
78
79    /// Returns the contained [`Ok`] value, consuming the `self` value.
80    #[inline]
81    #[track_caller]
82    pub fn expect(self, msg: &str) -> (T, B) {
83        (self.0.expect(msg), self.1)
84    }
85
86    /// Returns the contained [`Ok`] value, consuming the `self` value.
87    #[inline(always)]
88    #[track_caller]
89    pub fn unwrap(self) -> (T, B) {
90        (self.0.unwrap(), self.1)
91    }
92}
93
94impl<T, B> From<(io::Result<T>, B)> for BufResult<T, B> {
95    fn from((res, buf): (io::Result<T>, B)) -> Self {
96        Self(res, buf)
97    }
98}
99
100impl<T, B> From<BufResult<T, B>> for (io::Result<T>, B) {
101    fn from(BufResult(res, buf): BufResult<T, B>) -> Self {
102        (res, buf)
103    }
104}
105
106impl<T: IntoInner, O> IntoInner for BufResult<O, T> {
107    type Inner = BufResult<O, T::Inner>;
108
109    fn into_inner(self) -> Self::Inner {
110        BufResult(self.0, self.1.into_inner())
111    }
112}
113
114/// ```
115/// # use compio_buf::BufResult;
116/// fn foo() -> BufResult<i32, i32> {
117///     let (a, b) = BufResult(Ok(1), 2)?;
118///     assert_eq!(a, 1);
119///     assert_eq!(b, 2);
120///     (Ok(3), 4).into()
121/// }
122/// assert!(foo().is_ok());
123/// ```
124#[cfg(feature = "try_trait_v2")]
125impl<T, B> FromResidual<BufResult<Infallible, B>> for BufResult<T, B> {
126    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
127        match residual {
128            BufResult(Err(e), b) => BufResult(Err(e), b),
129        }
130    }
131}
132
133/// ```
134/// # use compio_buf::BufResult;
135/// fn foo() -> std::io::Result<i32> {
136///     let (a, b) = BufResult(Ok(1), 2)?;
137///     assert_eq!(a, 1);
138///     assert_eq!(b, 2);
139///     Ok(3)
140/// }
141/// assert!(foo().is_ok());
142/// ```
143#[cfg(feature = "try_trait_v2")]
144impl<T, B> FromResidual<BufResult<Infallible, B>> for io::Result<T> {
145    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
146        match residual {
147            BufResult(Err(e), _) => Err(e),
148        }
149    }
150}
151
152#[cfg(feature = "try_trait_v2")]
153impl<T, B> Try for BufResult<T, B> {
154    type Output = (T, B);
155    type Residual = BufResult<Infallible, B>;
156
157    fn from_output((res, buf): Self::Output) -> Self {
158        Self(Ok(res), buf)
159    }
160
161    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
162        match self {
163            BufResult(Ok(res), buf) => ControlFlow::Continue((res, buf)),
164            BufResult(Err(e), buf) => ControlFlow::Break(BufResult(Err(e), buf)),
165        }
166    }
167}
168
169#[cfg(feature = "try_trait_v2")]
170impl<T, B> Residual<(T, B)> for BufResult<Infallible, B> {
171    type TryType = BufResult<T, B>;
172}
173
174/// A helper macro to imitate the behavior of try trait `?`.
175/// ```
176/// # use compio_buf::{buf_try, BufResult};
177/// fn foo() -> BufResult<i32, i32> {
178///     let (a, b) = buf_try!(BufResult(Ok(1), 2));
179///     assert_eq!(a, 1);
180///     assert_eq!(b, 2);
181///     (Ok(3), 4).into()
182/// }
183/// assert!(foo().is_ok());
184/// ```
185#[macro_export]
186macro_rules! buf_try {
187    ($e:expr) => {{
188        match $e {
189            $crate::BufResult(Ok(res), buf) => (res, buf),
190            $crate::BufResult(Err(e), buf) => return $crate::BufResult(Err(e), buf),
191        }
192    }};
193    ($e:expr, $b:expr) => {{
194        let buf = $b;
195        match $e {
196            Ok(res) => (res, buf),
197            Err(e) => return $crate::BufResult(Err(e), buf),
198        }
199    }};
200    (@try $e:expr) => {{
201        let $crate::BufResult(res, buf) = $e;
202        (res?, buf)
203    }};
204}