1use futures::Stream;
11use std::collections::HashMap;
12use std::pin::Pin;
13use std::sync::Arc;
14use uni_common::{Result, UniError};
15
16#[doc(inline)]
18pub use uni_common::value::{Edge, FromValue, Node, Path, Value};
19
20#[derive(Debug, Clone)]
22pub struct Row {
23 pub columns: Arc<Vec<String>>,
25 pub values: Vec<Value>,
27}
28
29impl Row {
30 pub fn get<T: FromValue>(&self, column: &str) -> Result<T> {
37 let idx = self
38 .columns
39 .iter()
40 .position(|c| c == column)
41 .ok_or_else(|| UniError::Query {
42 message: format!("Column '{}' not found", column),
43 query: None,
44 })?;
45 self.get_idx(idx)
46 }
47
48 pub fn get_idx<T: FromValue>(&self, index: usize) -> Result<T> {
55 if index >= self.values.len() {
56 return Err(UniError::Query {
57 message: format!("Column index {} out of bounds", index),
58 query: None,
59 });
60 }
61 T::from_value(&self.values[index])
62 }
63
64 pub fn try_get<T: FromValue>(&self, column: &str) -> Option<T> {
66 self.get(column).ok()
67 }
68
69 pub fn value(&self, column: &str) -> Option<&Value> {
71 let idx = self.columns.iter().position(|c| c == column)?;
72 self.values.get(idx)
73 }
74
75 pub fn as_map(&self) -> HashMap<&str, &Value> {
77 self.columns
78 .iter()
79 .zip(&self.values)
80 .map(|(col, val)| (col.as_str(), val))
81 .collect()
82 }
83
84 pub fn to_json(&self) -> serde_json::Value {
86 serde_json::to_value(self.as_map()).unwrap_or(serde_json::Value::Null)
87 }
88}
89
90impl std::ops::Index<usize> for Row {
91 type Output = Value;
92 fn index(&self, index: usize) -> &Self::Output {
93 &self.values[index]
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
102pub enum QueryWarning {
103 IndexUnavailable {
105 label: String,
107 index_name: String,
109 reason: String,
111 },
112 NoIndexForFilter {
114 label: String,
116 property: String,
118 },
119 Other(String),
121}
122
123impl std::fmt::Display for QueryWarning {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 match self {
126 QueryWarning::IndexUnavailable {
127 label,
128 index_name,
129 reason,
130 } => {
131 write!(
132 f,
133 "Index '{}' on label '{}' is unavailable: {}",
134 index_name, label, reason
135 )
136 }
137 QueryWarning::NoIndexForFilter { label, property } => {
138 write!(
139 f,
140 "No index available for filter on {}.{}, using full scan",
141 label, property
142 )
143 }
144 QueryWarning::Other(msg) => write!(f, "{}", msg),
145 }
146 }
147}
148
149#[derive(Debug)]
151pub struct QueryResult {
152 pub columns: Arc<Vec<String>>,
154 pub rows: Vec<Row>,
156 pub warnings: Vec<QueryWarning>,
158}
159
160impl QueryResult {
161 pub fn columns(&self) -> &[String] {
163 &self.columns
164 }
165
166 pub fn len(&self) -> usize {
168 self.rows.len()
169 }
170
171 pub fn is_empty(&self) -> bool {
173 self.rows.is_empty()
174 }
175
176 pub fn rows(&self) -> &[Row] {
178 &self.rows
179 }
180
181 pub fn into_rows(self) -> Vec<Row> {
183 self.rows
184 }
185
186 pub fn iter(&self) -> impl Iterator<Item = &Row> {
188 self.rows.iter()
189 }
190
191 pub fn warnings(&self) -> &[QueryWarning] {
193 &self.warnings
194 }
195
196 pub fn has_warnings(&self) -> bool {
198 !self.warnings.is_empty()
199 }
200}
201
202impl IntoIterator for QueryResult {
203 type Item = Row;
204 type IntoIter = std::vec::IntoIter<Row>;
205
206 fn into_iter(self) -> Self::IntoIter {
207 self.rows.into_iter()
208 }
209}
210
211#[derive(Debug)]
213pub struct ExecuteResult {
214 pub affected_rows: usize,
216}
217
218pub struct QueryCursor {
220 pub columns: Arc<Vec<String>>,
222 pub stream: Pin<Box<dyn Stream<Item = Result<Vec<Row>>> + Send>>,
224}
225
226impl QueryCursor {
227 pub fn columns(&self) -> &[String] {
229 &self.columns
230 }
231
232 pub async fn next_batch(&mut self) -> Option<Result<Vec<Row>>> {
234 use futures::StreamExt;
235 self.stream.next().await
236 }
237
238 pub async fn collect_remaining(mut self) -> Result<Vec<Row>> {
244 use futures::StreamExt;
245 let mut rows = Vec::new();
246 while let Some(batch_res) = self.stream.next().await {
247 rows.extend(batch_res?);
248 }
249 Ok(rows)
250 }
251}