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}