use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::{error_detail::ErrorDetail, query_result::QueryResult, ResponseStatus};
use crate::models::KalamCellValue;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResponse {
pub status: ResponseStatus,
#[serde(default)]
pub results: Vec<QueryResult>,
#[serde(skip_serializing_if = "Option::is_none")]
pub took: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<ErrorDetail>,
}
impl QueryResponse {
pub fn success(&self) -> bool {
self.status == ResponseStatus::Success
}
pub fn is_token_expired(&self) -> bool {
self.status == ResponseStatus::Error
&& self.error.as_ref().is_some_and(|e| e.code == "TOKEN_EXPIRED")
}
pub fn rows(&self) -> Vec<Vec<KalamCellValue>> {
self.results.first().and_then(|r| r.rows.as_ref()).cloned().unwrap_or_default()
}
pub fn rows_as_maps(&self) -> Vec<HashMap<String, KalamCellValue>> {
let Some(result) = self.results.first() else {
return Vec::new();
};
let row_count = result.rows.as_ref().map(|r| r.len()).unwrap_or(0);
(0..row_count).filter_map(|i| result.row_as_map(i)).collect()
}
pub fn first_row_as_map(&self) -> Option<HashMap<String, KalamCellValue>> {
self.results.first().and_then(|r| r.row_as_map(0))
}
pub fn row_count(&self) -> usize {
self.results.first().map(|r| r.row_count).unwrap_or(0)
}
pub fn column_index(&self, column_name: &str) -> Option<usize> {
self.results
.first()
.and_then(|r| r.schema.iter().position(|f| f.name == column_name))
}
pub fn get_value(&self, column_name: &str) -> Option<KalamCellValue> {
self.first_row_as_map().and_then(|row| row.get(column_name).cloned())
}
pub fn get_i64(&self, column_name: &str) -> Option<i64> {
self.get_value(column_name).and_then(|v| match v.inner() {
serde_json::Value::Number(n) => n.as_i64(),
serde_json::Value::String(s) => s.parse::<i64>().ok(),
_ => None,
})
}
pub fn get_string(&self, column_name: &str) -> Option<String> {
self.get_value(column_name).and_then(|v| v.as_str().map(|s| s.to_string()))
}
}