use crate::Error;
use serde_json::Value as JsonValue;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum Value {
Null,
Bool(bool),
Integer(i64),
Float(f64),
String(String),
Array(Vec<Value>),
Object(HashMap<String, Value>),
}
impl Value {
pub fn from_json(json: JsonValue) -> Self {
match json {
JsonValue::Null => Value::Null,
JsonValue::Bool(b) => Value::Bool(b),
JsonValue::Number(n) => {
if let Some(i) = n.as_i64() {
Value::Integer(i)
} else if let Some(f) = n.as_f64() {
Value::Float(f)
} else {
Value::Null
}
}
JsonValue::String(s) => Value::String(s),
JsonValue::Array(arr) => {
Value::Array(arr.into_iter().map(Value::from_json).collect())
}
JsonValue::Object(obj) => {
Value::Object(obj.into_iter().map(|(k, v)| (k, Value::from_json(v))).collect())
}
}
}
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::Integer(i) => Some(*i),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Float(f) => Some(*f),
Value::Integer(i) => Some(*i as f64),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s),
_ => None,
}
}
pub fn get(&self, key: &str) -> Option<&Value> {
match self {
Value::Object(map) => map.get(key),
_ => None,
}
}
pub fn get_index(&self, index: usize) -> Option<&Value> {
match self {
Value::Array(arr) => arr.get(index),
_ => None,
}
}
pub fn as_array(&self) -> Option<&Vec<Value>> {
match self {
Value::Array(arr) => Some(arr),
_ => None,
}
}
pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
match self {
Value::Object(map) => Some(map),
_ => None,
}
}
}
impl std::ops::Index<&str> for Value {
type Output = Value;
fn index(&self, key: &str) -> &Self::Output {
self.get(key).unwrap_or_else(|| panic!("Value is not an Object or key '{}' not found", key))
}
}
#[derive(Debug, Clone)]
pub struct Row {
columns: Vec<String>,
values: HashMap<String, Value>,
}
impl Row {
pub(crate) fn from_json_object(obj: serde_json::Map<String, JsonValue>) -> Self {
let columns: Vec<String> = obj.keys().cloned().collect();
let values: HashMap<String, Value> = obj
.into_iter()
.map(|(k, v)| (k, Value::from_json(v)))
.collect();
Row { columns, values }
}
pub(crate) fn from_map(map: HashMap<String, Value>) -> Self {
let columns: Vec<String> = map.keys().cloned().collect();
Row { columns, values: map }
}
pub fn get_value(&self, column: &str) -> Option<&Value> {
self.values.get(column)
}
pub fn get<T: FromValue>(&self, column: &str) -> crate::Result<T> {
let value = self.values.get(column).ok_or_else(|| {
Error::ColumnNotFound(column.to_string())
})?;
T::from_value(value)
}
pub fn columns(&self) -> &[String] {
&self.columns
}
pub fn contains(&self, column: &str) -> bool {
self.values.contains_key(column)
}
}
pub trait FromValue: Sized {
fn from_value(value: &Value) -> crate::Result<Self>;
}
impl FromValue for String {
fn from_value(value: &Value) -> crate::Result<Self> {
match value {
Value::String(s) => Ok(s.clone()),
Value::Null => Ok(String::new()),
other => Err(Error::TypeError {
expected: "String",
actual: format!("{:?}", other),
}),
}
}
}
impl FromValue for i64 {
fn from_value(value: &Value) -> crate::Result<Self> {
match value {
Value::Integer(i) => Ok(*i),
Value::String(s) => s.parse::<i64>().map_err(|_| Error::TypeError {
expected: "Integer",
actual: format!("String({:?})", s),
}),
other => Err(Error::TypeError {
expected: "Integer",
actual: format!("{:?}", other),
}),
}
}
}
impl FromValue for i32 {
fn from_value(value: &Value) -> crate::Result<Self> {
match value {
Value::Integer(i) => Ok(*i as i32),
other => Err(Error::TypeError {
expected: "Integer",
actual: format!("{:?}", other),
}),
}
}
}
impl FromValue for f64 {
fn from_value(value: &Value) -> crate::Result<Self> {
match value {
Value::Float(f) => Ok(*f),
Value::Integer(i) => Ok(*i as f64),
other => Err(Error::TypeError {
expected: "Float",
actual: format!("{:?}", other),
}),
}
}
}
impl FromValue for bool {
fn from_value(value: &Value) -> crate::Result<Self> {
match value {
Value::Bool(b) => Ok(*b),
Value::Integer(1) => Ok(true),
Value::Integer(0) => Ok(false),
other => Err(Error::TypeError {
expected: "Bool",
actual: format!("{:?}", other),
}),
}
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: &Value) -> crate::Result<Self> {
match value {
Value::Null => Ok(None),
_ => Ok(Some(T::from_value(value)?)),
}
}
}
#[derive(Debug, Clone)]
pub struct CypherResult {
rows: Vec<Row>,
columns: Vec<String>,
}
impl CypherResult {
pub fn empty() -> Self {
CypherResult {
rows: Vec::new(),
columns: Vec::new(),
}
}
pub fn from_json(json_str: &str) -> crate::Result<Self> {
let trimmed = json_str.trim();
if trimmed.is_empty() {
return Ok(Self::empty());
}
let json: JsonValue = match serde_json::from_str(trimmed) {
Ok(v) => v,
Err(_) => {
if trimmed.starts_with("Error") || trimmed.starts_with("{\"error\"") {
if let Ok(v) = serde_json::from_str::<serde_json::Value>(trimmed) {
if let Some(msg) = v.get("error").and_then(|e| e.as_str()) {
return Err(crate::Error::Cypher(msg.to_string()));
}
}
return Err(crate::Error::Cypher(trimmed.to_string()));
}
return Ok(CypherResult {
rows: vec![Row::from_json_object({
let mut obj = serde_json::Map::new();
obj.insert("result".to_string(), JsonValue::String(trimmed.to_string()));
obj
})],
columns: vec!["result".to_string()],
});
}
};
match json {
JsonValue::Array(arr) => {
if arr.is_empty() {
return Ok(Self::empty());
}
let mut rows = Vec::with_capacity(arr.len());
let mut columns = Vec::new();
for (i, item) in arr.into_iter().enumerate() {
match item {
JsonValue::Object(obj) => {
if i == 0 {
columns = obj.keys().cloned().collect();
}
rows.push(Row::from_json_object(obj));
}
_ => {
let mut obj = serde_json::Map::new();
obj.insert("value".to_string(), item);
if i == 0 {
columns = vec!["value".to_string()];
}
rows.push(Row::from_json_object(obj));
}
}
}
Ok(CypherResult { rows, columns })
}
JsonValue::Object(obj) => {
let columns: Vec<String> = obj.keys().cloned().collect();
let row = Row::from_json_object(obj);
Ok(CypherResult {
rows: vec![row],
columns,
})
}
_ => {
let mut obj = serde_json::Map::new();
obj.insert("result".to_string(), json);
Ok(CypherResult {
rows: vec![Row::from_json_object(obj)],
columns: vec!["result".to_string()],
})
}
}
}
pub fn len(&self) -> usize {
self.rows.len()
}
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn columns(&self) -> &[String] {
&self.columns
}
pub fn get(&self, index: usize) -> Option<&Row> {
self.rows.get(index)
}
pub fn iter(&self) -> impl Iterator<Item = &Row> {
self.rows.iter()
}
}
impl<'a> IntoIterator for &'a CypherResult {
type Item = &'a Row;
type IntoIter = std::slice::Iter<'a, Row>;
fn into_iter(self) -> Self::IntoIter {
self.rows.iter()
}
}
impl IntoIterator for CypherResult {
type Item = Row;
type IntoIter = std::vec::IntoIter<Row>;
fn into_iter(self) -> Self::IntoIter {
self.rows.into_iter()
}
}
impl std::ops::Index<usize> for CypherResult {
type Output = Row;
fn index(&self, index: usize) -> &Self::Output {
&self.rows[index]
}
}