Skip to main content

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]
17#[derive(Debug)]
18pub struct BufResult<T, B>(pub io::Result<T>, pub B);
19
20impl<T, B> BufResult<T, B> {
21    /// Returns [`true`] if the result is [`Ok`].
22    pub const fn is_ok(&self) -> bool {
23        self.0.is_ok()
24    }
25
26    /// Returns [`true`] if the result is [`Err`].
27    pub const fn is_err(&self) -> bool {
28        self.0.is_err()
29    }
30
31    /// Maps the result part, and allows updating the buffer.
32    #[inline]
33    pub fn map<U>(self, f: impl FnOnce(T, B) -> (U, B)) -> BufResult<U, B> {
34        match self.0 {
35            Ok(res) => {
36                let (res, buf) = f(res, self.1);
37                BufResult(Ok(res), buf)
38            }
39            Err(e) => BufResult(Err(e), self.1),
40        }
41    }
42
43    /// Maps the result part, and allows changing the buffer type.
44    #[inline]
45    pub fn map2<U, C>(
46        self,
47        f_ok: impl FnOnce(T, B) -> (U, C),
48        f_err: impl FnOnce(B) -> C,
49    ) -> BufResult<U, C> {
50        match self.0 {
51            Ok(res) => {
52                let (res, buf) = f_ok(res, self.1);
53                BufResult(Ok(res), buf)
54            }
55            Err(e) => BufResult(Err(e), f_err(self.1)),
56        }
57    }
58
59    /// Maps the result part, and keeps the buffer unchanged.
60    #[inline]
61    pub fn map_res<U>(self, f: impl FnOnce(T) -> U) -> BufResult<U, B> {
62        BufResult(self.0.map(f), self.1)
63    }
64
65    /// Maps the buffer part, and keeps the result unchanged.
66    #[inline]
67    pub fn map_buffer<C>(self, f: impl FnOnce(B) -> C) -> BufResult<T, C> {
68        BufResult(self.0, f(self.1))
69    }
70
71    /// Updating the result type and modifying the buffer.
72    #[inline]
73    pub fn and_then<U>(self, f: impl FnOnce(T, B) -> (io::Result<U>, B)) -> BufResult<U, B> {
74        match self.0 {
75            Ok(res) => BufResult::from(f(res, self.1)),
76            Err(e) => BufResult(Err(e), self.1),
77        }
78    }
79
80    /// Returns the contained [`Ok`] value, consuming the `self` value.
81    #[inline]
82    #[track_caller]
83    pub fn expect(self, msg: &str) -> (T, B) {
84        (self.0.expect(msg), self.1)
85    }
86
87    /// Returns the contained [`Ok`] value, consuming the `self` value.
88    #[inline(always)]
89    #[track_caller]
90    pub fn unwrap(self) -> (T, B) {
91        (self.0.unwrap(), self.1)
92    }
93
94    /// Returns the parts, consuming the `self` value.
95    #[inline]
96    pub fn into_parts(self) -> (io::Result<T>, B) {
97        (self.0, self.1)
98    }
99}
100
101impl<T, B> From<(io::Result<T>, B)> for BufResult<T, B> {
102    fn from((res, buf): (io::Result<T>, B)) -> Self {
103        Self(res, buf)
104    }
105}
106
107impl<T, B> From<BufResult<T, B>> for (io::Result<T>, B) {
108    fn from(BufResult(res, buf): BufResult<T, B>) -> Self {
109        (res, buf)
110    }
111}
112
113impl<T: IntoInner, O> IntoInner for BufResult<O, T> {
114    type Inner = BufResult<O, T::Inner>;
115
116    fn into_inner(self) -> Self::Inner {
117        BufResult(self.0, self.1.into_inner())
118    }
119}
120
121/// ```
122/// # use compio_buf::BufResult;
123/// fn foo() -> BufResult<i32, i32> {
124///     let (a, b) = BufResult(Ok(1), 2)?;
125///     assert_eq!(a, 1);
126///     assert_eq!(b, 2);
127///     (Ok(3), 4).into()
128/// }
129/// assert!(foo().is_ok());
130/// ```
131#[cfg(feature = "try_trait_v2")]
132impl<T, B> FromResidual<BufResult<Infallible, B>> for BufResult<T, B> {
133    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
134        match residual {
135            BufResult(Err(e), b) => BufResult(Err(e), b),
136        }
137    }
138}
139
140/// ```
141/// # use compio_buf::BufResult;
142/// fn foo() -> std::io::Result<i32> {
143///     let (a, b) = BufResult(Ok(1), 2)?;
144///     assert_eq!(a, 1);
145///     assert_eq!(b, 2);
146///     Ok(3)
147/// }
148/// assert!(foo().is_ok());
149/// ```
150#[cfg(feature = "try_trait_v2")]
151impl<T, B, E> FromResidual<BufResult<Infallible, B>> for Result<T, E>
152where
153    E: From<io::Error>,
154{
155    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
156        match residual {
157            BufResult(Err(e), _) => Err(e.into()),
158        }
159    }
160}
161
162#[cfg(feature = "try_trait_v2")]
163impl<T, B> Try for BufResult<T, B> {
164    type Output = (T, B);
165    type Residual = BufResult<Infallible, B>;
166
167    fn from_output((res, buf): Self::Output) -> Self {
168        Self(Ok(res), buf)
169    }
170
171    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
172        match self {
173            BufResult(Ok(res), buf) => ControlFlow::Continue((res, buf)),
174            BufResult(Err(e), buf) => ControlFlow::Break(BufResult(Err(e), buf)),
175        }
176    }
177}
178
179#[cfg(feature = "try_trait_v2")]
180impl<T, B> Residual<(T, B)> for BufResult<Infallible, B> {
181    type TryType = BufResult<T, B>;
182}
183
184/// A helper macro to imitate the behavior of try trait `?`.
185/// ```
186/// # use compio_buf::{buf_try, BufResult};
187/// fn foo() -> BufResult<i32, i32> {
188///     let (a, b) = buf_try!(BufResult(Ok(1), 2));
189///     assert_eq!(a, 1);
190///     assert_eq!(b, 2);
191///     (Ok(3), 4).into()
192/// }
193/// assert!(foo().is_ok());
194/// ```
195#[macro_export]
196macro_rules! buf_try {
197    ($e:expr) => {{
198        match $e {
199            $crate::BufResult(Ok(res), buf) => (res, buf),
200            $crate::BufResult(Err(e), buf) => return $crate::BufResult(Err(e), buf),
201        }
202    }};
203    ($e:expr, $b:expr) => {{
204        let buf = $b;
205        match $e {
206            Ok(res) => (res, buf),
207            Err(e) => return $crate::BufResult(Err(e), buf),
208        }
209    }};
210    (@try $e:expr) => {{
211        let $crate::BufResult(res, buf) = $e;
212        (res?, buf)
213    }};
214}
215
216#[cfg(test)]
217mod test {
218    #[cfg(feature = "try_trait_v2")]
219    #[test]
220    fn test_buf_result_try() {
221        use crate::BufResult;
222
223        fn returns_buf_result() -> BufResult<i32, i32> {
224            let (a, b) = BufResult(Ok(1), 2)?;
225            assert_eq!(a, 1);
226            assert_eq!(b, 2);
227            (Ok(3), 4).into()
228        }
229        assert!(returns_buf_result().is_ok());
230
231        fn returns_result() -> std::io::Result<i32> {
232            let (a, b) = BufResult(Ok(1), 2)?;
233            assert_eq!(a, 1);
234            assert_eq!(b, 2);
235            Ok(3)
236        }
237        assert!(returns_result().is_ok());
238
239        struct MyError;
240
241        impl From<std::io::Error> for MyError {
242            fn from(_: std::io::Error) -> Self {
243                MyError
244            }
245        }
246
247        fn returns_custom_result() -> Result<i32, MyError> {
248            let (a, b) = BufResult(Ok(1), 2)?;
249            assert_eq!(a, 1);
250            assert_eq!(b, 2);
251            Ok(3)
252        }
253        assert!(returns_custom_result().is_ok());
254    }
255}