use crate::error::{Error, Result};
use graphlite::{QueryResult, Row, Value};
use serde::de::DeserializeOwned;
pub struct TypedResult {
inner: QueryResult,
}
impl TypedResult {
pub fn new(result: QueryResult) -> Self {
TypedResult { inner: result }
}
pub fn inner(&self) -> &QueryResult {
&self.inner
}
pub fn into_inner(self) -> QueryResult {
self.inner
}
pub fn row_count(&self) -> usize {
self.inner.rows.len()
}
pub fn column_names(&self) -> Vec<String> {
self.inner.variables.clone()
}
pub fn get_row(&self, index: usize) -> Option<&Row> {
self.inner.rows.get(index)
}
pub fn deserialize_rows<T: DeserializeOwned>(&self) -> Result<Vec<T>> {
let mut results = Vec::new();
for row in &self.inner.rows {
let item = self.deserialize_row::<T>(row)?;
results.push(item);
}
Ok(results)
}
pub fn deserialize_row<T: DeserializeOwned>(&self, row: &Row) -> Result<T> {
let json_value = serde_json::to_value(&row.values)?;
let result = serde_json::from_value(json_value)?;
Ok(result)
}
pub fn first<T: DeserializeOwned>(&self) -> Result<T> {
let row = self
.get_row(0)
.ok_or_else(|| Error::NotFound("No rows returned".to_string()))?;
self.deserialize_row(row)
}
pub fn scalar<T: DeserializeOwned>(&self) -> Result<T> {
let row = self
.get_row(0)
.ok_or_else(|| Error::NotFound("No rows returned".to_string()))?;
let columns = &self.inner.variables;
if columns.is_empty() {
return Err(Error::NotFound("No columns returned".to_string()));
}
let value = row
.get_value(&columns[0])
.ok_or_else(|| Error::NotFound("Column value not found".to_string()))?;
value_to_type(value)
}
pub fn is_empty(&self) -> bool {
self.inner.rows.is_empty()
}
pub fn rows(&self) -> &[Row] {
&self.inner.rows
}
}
impl From<QueryResult> for TypedResult {
fn from(result: QueryResult) -> Self {
TypedResult::new(result)
}
}
fn value_to_type<T: DeserializeOwned>(value: &Value) -> Result<T> {
let json_value = value_to_json(value);
serde_json::from_value(json_value).map_err(|e| e.into())
}
fn value_to_json(value: &Value) -> serde_json::Value {
match value {
Value::Null => serde_json::Value::Null,
Value::Boolean(b) => serde_json::Value::Bool(*b),
Value::Number(n) => serde_json::json!(n),
Value::String(s) => serde_json::Value::String(s.clone()),
Value::Array(arr) | Value::List(arr) => {
let items: Vec<serde_json::Value> = arr.iter().map(|v| value_to_json(v)).collect();
serde_json::Value::Array(items)
}
_ => serde_json::to_value(value).unwrap_or(serde_json::Value::Null),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_conversion() {
let value = Value::Number(42.0);
let json = value_to_json(&value);
assert_eq!(json, serde_json::json!(42.0));
}
}