Skip to main content

compio_buf/
buf_result.rs

1#[cfg(feature = "try_trait_v2")]
2use std::{
3    convert::Infallible,
4    ops::{ControlFlow, FromResidual, Residual, Try},
5};
6use std::{fmt::Debug, io};
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: Debug, B> BufResult<T, B> {
102    /// Returns the contained [`io::Error`] value, consuming the `self` value.
103    #[inline(always)]
104    #[track_caller]
105    pub fn unwrap_err(self) -> (io::Error, B) {
106        (self.0.unwrap_err(), self.1)
107    }
108}
109
110impl<T, B> From<(io::Result<T>, B)> for BufResult<T, B> {
111    fn from((res, buf): (io::Result<T>, B)) -> Self {
112        Self(res, buf)
113    }
114}
115
116impl<T, B> From<BufResult<T, B>> for (io::Result<T>, B) {
117    fn from(BufResult(res, buf): BufResult<T, B>) -> Self {
118        (res, buf)
119    }
120}
121
122impl<T: IntoInner, O> IntoInner for BufResult<O, T> {
123    type Inner = BufResult<O, T::Inner>;
124
125    fn into_inner(self) -> Self::Inner {
126        BufResult(self.0, self.1.into_inner())
127    }
128}
129
130/// ```
131/// # use compio_buf::BufResult;
132/// fn foo() -> BufResult<i32, i32> {
133///     let (a, b) = BufResult(Ok(1), 2)?;
134///     assert_eq!(a, 1);
135///     assert_eq!(b, 2);
136///     (Ok(3), 4).into()
137/// }
138/// assert!(foo().is_ok());
139/// ```
140#[cfg(feature = "try_trait_v2")]
141impl<T, B> FromResidual<BufResult<Infallible, B>> for BufResult<T, B> {
142    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
143        match residual {
144            BufResult(Err(e), b) => BufResult(Err(e), b),
145        }
146    }
147}
148
149/// ```
150/// # use compio_buf::BufResult;
151/// fn foo() -> std::io::Result<i32> {
152///     let (a, b) = BufResult(Ok(1), 2)?;
153///     assert_eq!(a, 1);
154///     assert_eq!(b, 2);
155///     Ok(3)
156/// }
157/// assert!(foo().is_ok());
158/// ```
159#[cfg(feature = "try_trait_v2")]
160impl<T, B, E> FromResidual<BufResult<Infallible, B>> for Result<T, E>
161where
162    E: From<io::Error>,
163{
164    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
165        match residual {
166            BufResult(Err(e), _) => Err(e.into()),
167        }
168    }
169}
170
171#[cfg(feature = "try_trait_v2")]
172impl<T, B> Try for BufResult<T, B> {
173    type Output = (T, B);
174    type Residual = BufResult<Infallible, B>;
175
176    fn from_output((res, buf): Self::Output) -> Self {
177        Self(Ok(res), buf)
178    }
179
180    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
181        match self {
182            BufResult(Ok(res), buf) => ControlFlow::Continue((res, buf)),
183            BufResult(Err(e), buf) => ControlFlow::Break(BufResult(Err(e), buf)),
184        }
185    }
186}
187
188#[cfg(feature = "try_trait_v2")]
189impl<T, B> Residual<(T, B)> for BufResult<Infallible, B> {
190    type TryType = BufResult<T, B>;
191}
192
193/// A helper macro to imitate the behavior of try trait `?`.
194/// ```
195/// # use compio_buf::{buf_try, BufResult};
196/// fn foo() -> BufResult<i32, i32> {
197///     let (a, b) = buf_try!(BufResult(Ok(1), 2));
198///     assert_eq!(a, 1);
199///     assert_eq!(b, 2);
200///     (Ok(3), 4).into()
201/// }
202/// assert!(foo().is_ok());
203/// ```
204#[macro_export]
205macro_rules! buf_try {
206    ($e:expr) => {{
207        match $e {
208            $crate::BufResult(Ok(res), buf) => (res, buf),
209            $crate::BufResult(Err(e), buf) => return $crate::BufResult(Err(e), buf),
210        }
211    }};
212    ($e:expr, $b:expr) => {{
213        let buf = $b;
214        match $e {
215            Ok(res) => (res, buf),
216            Err(e) => return $crate::BufResult(Err(e), buf),
217        }
218    }};
219    (@try $e:expr) => {{
220        let $crate::BufResult(res, buf) = $e;
221        (res?, buf)
222    }};
223}
224
225#[cfg(test)]
226mod test {
227    #[cfg(feature = "try_trait_v2")]
228    #[test]
229    fn test_buf_result_try() {
230        use crate::BufResult;
231
232        fn returns_buf_result() -> BufResult<i32, i32> {
233            let (a, b) = BufResult(Ok(1), 2)?;
234            assert_eq!(a, 1);
235            assert_eq!(b, 2);
236            (Ok(3), 4).into()
237        }
238        assert!(returns_buf_result().is_ok());
239
240        fn returns_result() -> std::io::Result<i32> {
241            let (a, b) = BufResult(Ok(1), 2)?;
242            assert_eq!(a, 1);
243            assert_eq!(b, 2);
244            Ok(3)
245        }
246        assert!(returns_result().is_ok());
247
248        struct MyError;
249
250        impl From<std::io::Error> for MyError {
251            fn from(_: std::io::Error) -> Self {
252                MyError
253            }
254        }
255
256        fn returns_custom_result() -> Result<i32, MyError> {
257            let (a, b) = BufResult(Ok(1), 2)?;
258            assert_eq!(a, 1);
259            assert_eq!(b, 2);
260            Ok(3)
261        }
262        assert!(returns_custom_result().is_ok());
263    }
264}