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}