Skip to main content

toasty_core/driver/
response.rs

1use crate::{Result, stmt};
2
3/// The result of a database operation.
4///
5/// Every database operation produces an `ExecResponse` containing [`Rows`],
6/// which may be a row count, a single value, or a stream of result rows.
7/// Paginated queries may also include cursors for fetching subsequent pages.
8///
9/// # Examples
10///
11/// ```
12/// use toasty_core::driver::ExecResponse;
13///
14/// // Create a count response (e.g., from a DELETE that affected 3 rows)
15/// let resp = ExecResponse::count(3);
16/// assert_eq!(resp.values.into_count(), 3);
17/// ```
18#[derive(Debug)]
19pub struct ExecResponse {
20    /// The result values (rows, count, or stream).
21    pub values: Rows,
22    /// Cursor to the next page (if paginated and more data exists).
23    pub next_cursor: Option<Box<stmt::Value>>,
24    /// Cursor to the previous page (if backward pagination is supported).
25    pub prev_cursor: Option<Box<stmt::Value>>,
26}
27
28/// The payload of an [`ExecResponse`].
29///
30/// Operations that modify rows typically return [`Count`](Self::Count).
31/// Queries return either a single [`Value`](Self::Value) or a
32/// [`Stream`](Self::Stream) of rows.
33#[derive(Debug)]
34pub enum Rows {
35    /// Number of rows affected by the operation (e.g., rows deleted or updated).
36    Count(u64),
37
38    /// A single value result.
39    Value(stmt::Value),
40
41    /// A stream of result rows, consumed asynchronously.
42    Stream(stmt::ValueStream),
43}
44
45impl ExecResponse {
46    /// Returns `true` if this response has no pagination cursors.
47    pub fn is_unpaginated(&self) -> bool {
48        self.next_cursor.is_none() && self.prev_cursor.is_none()
49    }
50
51    /// Creates a response indicating that `count` rows were affected.
52    pub fn count(count: u64) -> Self {
53        Self {
54            values: Rows::Count(count),
55            next_cursor: None,
56            prev_cursor: None,
57        }
58    }
59
60    /// Creates a response wrapping a stream of values.
61    pub fn value_stream(values: impl Into<stmt::ValueStream>) -> Self {
62        Self {
63            values: Rows::value_stream(values),
64            next_cursor: None,
65            prev_cursor: None,
66        }
67    }
68
69    /// Creates a response with an empty value stream (no rows).
70    pub fn empty_value_stream() -> Self {
71        Self {
72            values: Rows::Stream(stmt::ValueStream::default()),
73            next_cursor: None,
74            prev_cursor: None,
75        }
76    }
77
78    /// Create a response from rows with no pagination cursors.
79    pub fn from_rows(rows: Rows) -> Self {
80        Self {
81            values: rows,
82            next_cursor: None,
83            prev_cursor: None,
84        }
85    }
86}
87
88impl Rows {
89    /// Wraps the given values as a [`Stream`](Self::Stream).
90    pub fn value_stream(values: impl Into<stmt::ValueStream>) -> Self {
91        Self::Stream(values.into())
92    }
93
94    /// Returns `true` if this is a [`Count`](Self::Count) variant.
95    pub fn is_count(&self) -> bool {
96        matches!(self, Self::Count(_))
97    }
98
99    /// If this is a [`Stream`](Self::Stream), collects all values and converts
100    /// it to a [`Value`](Self::Value) containing a [`Value::List`](stmt::Value::List).
101    /// Other variants are left unchanged.
102    pub async fn buffer(&mut self) -> Result<()> {
103        if matches!(self, Rows::Stream(_)) {
104            let Rows::Stream(stream) = std::mem::replace(self, Rows::Count(0)) else {
105                unreachable!()
106            };
107            *self = Rows::Value(stmt::Value::List(stream.collect().await?));
108        }
109        Ok(())
110    }
111
112    /// Creates a duplicate of this `Rows` value.
113    ///
114    /// For streams, this buffers the stream contents so both the original and
115    /// the duplicate can be consumed independently.
116    pub async fn dup(&mut self) -> Result<Self> {
117        match self {
118            Rows::Count(count) => Ok(Rows::Count(*count)),
119            Rows::Value(value) => Ok(Rows::Value(value.clone())),
120            Rows::Stream(values) => Ok(Rows::Stream(values.dup().await?)),
121        }
122    }
123
124    /// Attempts to clone this `Rows` value without async buffering.
125    ///
126    /// Returns `None` if the stream variant cannot be cloned synchronously.
127    pub fn try_clone(&self) -> Option<Self> {
128        match self {
129            Rows::Count(count) => Some(Rows::Count(*count)),
130            Rows::Value(value) => Some(Rows::Value(value.clone())),
131            Rows::Stream(values) => values.try_clone().map(Rows::Stream),
132        }
133    }
134
135    /// Consumes this `Rows` and returns the count.
136    ///
137    /// # Panics
138    ///
139    /// Panics if this is not a [`Count`](Self::Count) variant.
140    #[track_caller]
141    pub fn into_count(self) -> u64 {
142        match self {
143            Rows::Count(count) => count,
144            _ => todo!("rows={self:#?}"),
145        }
146    }
147
148    /// Collects all rows into a single [`Value::List`](stmt::Value::List).
149    ///
150    /// For [`Stream`](Self::Stream) variants, this consumes the entire stream.
151    /// For [`Value`](Self::Value) variants, returns the value directly.
152    ///
153    /// # Panics
154    ///
155    /// Panics if this is a [`Count`](Self::Count) variant.
156    pub async fn collect_as_value(self) -> Result<stmt::Value> {
157        match self {
158            Rows::Count(_) => panic!("expected value; actual={self:#?}"),
159            Rows::Value(value) => Ok(value),
160            Rows::Stream(stream) => Ok(stmt::Value::List(stream.collect().await?)),
161        }
162    }
163
164    /// Converts this `Rows` into a [`ValueStream`](stmt::ValueStream).
165    ///
166    /// [`Value::List`](stmt::Value::List) variants are converted into a stream
167    /// from the list items.
168    ///
169    /// # Panics
170    ///
171    /// Panics if this is a [`Count`](Self::Count) variant.
172    pub fn into_value_stream(self) -> stmt::ValueStream {
173        match self {
174            Rows::Value(stmt::Value::List(items)) => stmt::ValueStream::from_vec(items),
175            Rows::Stream(stream) => stream,
176            _ => panic!("expected ValueStream; actual={self:#?}"),
177        }
178    }
179}