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
95impl<T, B> From<(io::Result<T>, B)> for BufResult<T, B> {
96    fn from((res, buf): (io::Result<T>, B)) -> Self {
97        Self(res, buf)
98    }
99}
100
101impl<T, B> From<BufResult<T, B>> for (io::Result<T>, B) {
102    fn from(BufResult(res, buf): BufResult<T, B>) -> Self {
103        (res, buf)
104    }
105}
106
107impl<T: IntoInner, O> IntoInner for BufResult<O, T> {
108    type Inner = BufResult<O, T::Inner>;
109
110    fn into_inner(self) -> Self::Inner {
111        BufResult(self.0, self.1.into_inner())
112    }
113}
114
115/// ```
116/// # use compio_buf::BufResult;
117/// fn foo() -> BufResult<i32, i32> {
118///     let (a, b) = BufResult(Ok(1), 2)?;
119///     assert_eq!(a, 1);
120///     assert_eq!(b, 2);
121///     (Ok(3), 4).into()
122/// }
123/// assert!(foo().is_ok());
124/// ```
125#[cfg(feature = "try_trait_v2")]
126impl<T, B> FromResidual<BufResult<Infallible, B>> for BufResult<T, B> {
127    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
128        match residual {
129            BufResult(Err(e), b) => BufResult(Err(e), b),
130        }
131    }
132}
133
134/// ```
135/// # use compio_buf::BufResult;
136/// fn foo() -> std::io::Result<i32> {
137///     let (a, b) = BufResult(Ok(1), 2)?;
138///     assert_eq!(a, 1);
139///     assert_eq!(b, 2);
140///     Ok(3)
141/// }
142/// assert!(foo().is_ok());
143/// ```
144#[cfg(feature = "try_trait_v2")]
145impl<T, B> FromResidual<BufResult<Infallible, B>> for io::Result<T> {
146    fn from_residual(residual: BufResult<Infallible, B>) -> Self {
147        match residual {
148            BufResult(Err(e), _) => Err(e),
149        }
150    }
151}
152
153#[cfg(feature = "try_trait_v2")]
154impl<T, B> Try for BufResult<T, B> {
155    type Output = (T, B);
156    type Residual = BufResult<Infallible, B>;
157
158    fn from_output((res, buf): Self::Output) -> Self {
159        Self(Ok(res), buf)
160    }
161
162    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
163        match self {
164            BufResult(Ok(res), buf) => ControlFlow::Continue((res, buf)),
165            BufResult(Err(e), buf) => ControlFlow::Break(BufResult(Err(e), buf)),
166        }
167    }
168}
169
170#[cfg(feature = "try_trait_v2")]
171impl<T, B> Residual<(T, B)> for BufResult<Infallible, B> {
172    type TryType = BufResult<T, B>;
173}
174
175/// A helper macro to imitate the behavior of try trait `?`.
176/// ```
177/// # use compio_buf::{buf_try, BufResult};
178/// fn foo() -> BufResult<i32, i32> {
179///     let (a, b) = buf_try!(BufResult(Ok(1), 2));
180///     assert_eq!(a, 1);
181///     assert_eq!(b, 2);
182///     (Ok(3), 4).into()
183/// }
184/// assert!(foo().is_ok());
185/// ```
186#[macro_export]
187macro_rules! buf_try {
188    ($e:expr) => {{
189        match $e {
190            $crate::BufResult(Ok(res), buf) => (res, buf),
191            $crate::BufResult(Err(e), buf) => return $crate::BufResult(Err(e), buf),
192        }
193    }};
194    ($e:expr, $b:expr) => {{
195        let buf = $b;
196        match $e {
197            Ok(res) => (res, buf),
198            Err(e) => return $crate::BufResult(Err(e), buf),
199        }
200    }};
201    (@try $e:expr) => {{
202        let $crate::BufResult(res, buf) = $e;
203        (res?, buf)
204    }};
205}