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<stmt::Value>,
24    /// Cursor to the previous page (if backward pagination is supported).
25    pub prev_cursor: Option<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    /// Creates a response indicating that `count` rows were affected.
47    pub fn count(count: u64) -> Self {
48        Self {
49            values: Rows::Count(count),
50            next_cursor: None,
51            prev_cursor: None,
52        }
53    }
54
55    /// Creates a response wrapping a stream of values.
56    pub fn value_stream(values: impl Into<stmt::ValueStream>) -> Self {
57        Self {
58            values: Rows::value_stream(values),
59            next_cursor: None,
60            prev_cursor: None,
61        }
62    }
63
64    /// Creates a response with an empty value stream (no rows).
65    pub fn empty_value_stream() -> Self {
66        Self {
67            values: Rows::Stream(stmt::ValueStream::default()),
68            next_cursor: None,
69            prev_cursor: None,
70        }
71    }
72
73    /// Create a response from rows with no pagination cursors.
74    pub fn from_rows(rows: Rows) -> Self {
75        Self {
76            values: rows,
77            next_cursor: None,
78            prev_cursor: None,
79        }
80    }
81}
82
83impl Rows {
84    /// Wraps the given values as a [`Stream`](Self::Stream).
85    pub fn value_stream(values: impl Into<stmt::ValueStream>) -> Self {
86        Self::Stream(values.into())
87    }
88
89    /// Returns `true` if this is a [`Count`](Self::Count) variant.
90    pub fn is_count(&self) -> bool {
91        matches!(self, Self::Count(_))
92    }
93
94    /// If this is a [`Stream`](Self::Stream), collects all values and converts
95    /// it to a [`Value`](Self::Value) containing a [`Value::List`](stmt::Value::List).
96    /// Other variants are left unchanged.
97    pub async fn buffer(&mut self) -> Result<()> {
98        if matches!(self, Rows::Stream(_)) {
99            let Rows::Stream(stream) = std::mem::replace(self, Rows::Count(0)) else {
100                unreachable!()
101            };
102            *self = Rows::Value(stmt::Value::List(stream.collect().await?));
103        }
104        Ok(())
105    }
106
107    /// Creates a duplicate of this `Rows` value.
108    ///
109    /// For streams, this buffers the stream contents so both the original and
110    /// the duplicate can be consumed independently.
111    pub async fn dup(&mut self) -> Result<Self> {
112        match self {
113            Rows::Count(count) => Ok(Rows::Count(*count)),
114            Rows::Value(value) => Ok(Rows::Value(value.clone())),
115            Rows::Stream(values) => Ok(Rows::Stream(values.dup().await?)),
116        }
117    }
118
119    /// Attempts to clone this `Rows` value without async buffering.
120    ///
121    /// Returns `None` if the stream variant cannot be cloned synchronously.
122    pub fn try_clone(&self) -> Option<Self> {
123        match self {
124            Rows::Count(count) => Some(Rows::Count(*count)),
125            Rows::Value(value) => Some(Rows::Value(value.clone())),
126            Rows::Stream(values) => values.try_clone().map(Rows::Stream),
127        }
128    }
129
130    /// Consumes this `Rows` and returns the count.
131    ///
132    /// # Panics
133    ///
134    /// Panics if this is not a [`Count`](Self::Count) variant.
135    #[track_caller]
136    pub fn into_count(self) -> u64 {
137        match self {
138            Rows::Count(count) => count,
139            _ => todo!("rows={self:#?}"),
140        }
141    }
142
143    /// Collects all rows into a single [`Value::List`](stmt::Value::List).
144    ///
145    /// For [`Stream`](Self::Stream) variants, this consumes the entire stream.
146    /// For [`Value`](Self::Value) variants, returns the value directly.
147    ///
148    /// # Panics
149    ///
150    /// Panics if this is a [`Count`](Self::Count) variant.
151    pub async fn collect_as_value(self) -> Result<stmt::Value> {
152        match self {
153            Rows::Count(_) => panic!("expected value; actual={self:#?}"),
154            Rows::Value(value) => Ok(value),
155            Rows::Stream(stream) => Ok(stmt::Value::List(stream.collect().await?)),
156        }
157    }
158
159    /// Converts this `Rows` into a [`ValueStream`](stmt::ValueStream).
160    ///
161    /// [`Value::List`](stmt::Value::List) variants are converted into a stream
162    /// from the list items.
163    ///
164    /// # Panics
165    ///
166    /// Panics if this is a [`Count`](Self::Count) variant.
167    pub fn into_value_stream(self) -> stmt::ValueStream {
168        match self {
169            Rows::Value(stmt::Value::List(items)) => stmt::ValueStream::from_vec(items),
170            Rows::Stream(stream) => stream,
171            _ => panic!("expected ValueStream; actual={self:#?}"),
172        }
173    }
174}