use std::collections::HashMap;
#[macro_use]
extern crate serde_derive;
mod conversions;
mod errors;
pub mod protocol;
pub use crate::errors::GraphResult;
pub use errors::GraphError;
pub const CAPID_GRAPHDB: &str = "wascc:graphdb";
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResultSet {
pub columns: Vec<Column>,
pub statistics: Statistics,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Statistics(pub Vec<String>);
impl ResultSet {
pub fn num_columns(&self) -> usize {
self.columns.len()
}
pub fn num_rows(&self) -> usize {
match self.columns.get(0) {
Some(first_column) => first_column.len(),
None => 0,
}
}
pub fn get_scalar(&self, row_idx: usize, column_idx: usize) -> GraphResult<&Scalar> {
match self.columns.get(column_idx) {
Some(column) => match column {
Column::Scalars(cells) => match cells.get(row_idx) {
Some(cell) => Ok(cell),
None => client_type_error!(
"failed to get scalar: row index out of bounds: the len is {:?} but the index is {:?}", self.columns.len(), column_idx,
),
},
any => client_type_error!(
"failed to get scalar: expected column of scalars, found {:?}",
any
),
}
None => client_type_error!(
"failed to get scalar: column index out of bounds: the len is {:?} but the index is {:?}", self.columns.len(), column_idx,
),
}
}
pub fn get_node(&self, row_idx: usize, column_idx: usize) -> GraphResult<&Node> {
match self.columns.get(column_idx) {
Some(column) => match column {
Column::Nodes(cells) => match cells.get(row_idx) {
Some(cell) => Ok(cell),
None => client_type_error!(
"failed to get node: row index out of bounds: the len is {:?} but the index is {:?}", self.columns.len(), column_idx,
),
},
any => client_type_error!(
"failed to get node: expected column of nodes, found {:?}",
any
),
}
None => client_type_error!(
"failed to get node: column index out of bounds: the len is {:?} but the index is {:?}", self.columns.len(), column_idx,
),
}
}
pub fn get_relation(&self, row_idx: usize, column_idx: usize) -> GraphResult<&Relation> {
match self.columns.get(column_idx) {
Some(column) => match column {
Column::Relations(cells) => match cells.get(row_idx) {
Some(cell) => Ok(cell),
None => client_type_error!(
"failed to get relation: row index out of bounds: the len is {:?} but the index is {:?}", self.columns.len(), column_idx,
),
},
any => client_type_error!(
"failed to get relation: expected column of relations, found {:?}",
any
),
}
None => client_type_error!(
"failed to get relation: column index out of bounds: the len is {:?} but the index is {:?}", self.columns.len(), column_idx,
),
}
}
}
impl FromTable for ResultSet {
fn from_table(result_set: &ResultSet) -> GraphResult<Self> {
Ok(result_set.clone())
}
}
impl<T: FromRow> FromTable for Vec<T> {
fn from_table(result_set: &ResultSet) -> GraphResult<Self> {
let num_rows = result_set.num_rows();
let mut ret = Self::with_capacity(num_rows);
for i in 0..num_rows {
ret.push(T::from_row(result_set, i)?);
}
Ok(ret)
}
}
pub trait FromTable: Sized {
fn from_table(result_set: &ResultSet) -> GraphResult<Self>;
}
pub trait FromRow: Sized {
fn from_row(result_set: &ResultSet, row_idx: usize) -> GraphResult<Self>;
}
pub trait FromCell: Sized {
fn from_cell(result_set: &ResultSet, row_idx: usize, column_idx: usize) -> GraphResult<Self>;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Column {
Scalars(Vec<Scalar>),
Nodes(Vec<Node>),
Relations(Vec<Relation>),
}
impl Column {
pub fn len(&self) -> usize {
match self {
Self::Scalars(cells) => cells.len(),
Self::Nodes(cells) => cells.len(),
Self::Relations(cells) => cells.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Serialize, Debug, Deserialize)]
enum ColumnType {
Unknown = 0,
Scalar = 1,
Node = 2,
Relation = 3,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Relation {
pub type_name: String,
pub properties: HashMap<String, Scalar>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Scalar {
Nil,
Boolean(bool),
Integer(i64),
Double(f64),
String(GraphString),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum ScalarType {
Unknown = 0,
Nil = 1,
String = 2,
Integer = 3,
Boolean = 4,
Double = 5,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct GraphString(pub Vec<u8>);
impl From<String> for GraphString {
fn from(string: String) -> Self {
Self(string.into_bytes())
}
}
impl From<Vec<u8>> for GraphString {
fn from(bytes: Vec<u8>) -> Self {
Self(bytes)
}
}
impl From<GraphString> for Vec<u8> {
fn from(redis_string: GraphString) -> Self {
redis_string.0
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Node {
pub labels: Vec<String>,
pub properties: HashMap<String, Scalar>,
}
macro_rules! impl_row_for_tuple {
() => ();
($($name:ident,)+) => (
#[doc(hidden)]
impl<$($name: FromCell),*> FromRow for ($($name,)*) {
#[allow(non_snake_case, unused_variables, clippy::eval_order_dependence)]
fn from_row(result_set: &ResultSet, row_idx: usize) -> GraphResult<($($name,)*)> {
let mut n = 0;
$(let $name = (); n += 1;)*
if result_set.num_columns() != n {
return client_type_error!(
"failed to construct tuple: tuple has {:?} entries but result table has {:?} columns",
n,
result_set.num_columns()
);
}
let mut i = 0;
Ok(($({let $name = (); $name::from_cell(result_set, row_idx, { i += 1; i - 1 })?},)*))
}
}
impl_row_for_tuple_peel!($($name,)*);
)
}
macro_rules! impl_row_for_tuple_peel {
($name:ident, $($other:ident,)*) => (impl_row_for_tuple!($($other,)*);)
}
impl_row_for_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
impl<T: FromCell> FromRow for T {
fn from_row(result_set: &ResultSet, row_idx: usize) -> GraphResult<Self> {
T::from_cell(result_set, row_idx, 0)
}
}
impl<T: FromRow> FromTable for T {
fn from_table(result_set: &ResultSet) -> GraphResult<Self> {
T::from_row(result_set, 0)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn tuple_extraction_test() {
let (name, birth_year): (String, u32) = fake_query("fake query").unwrap();
assert_eq!("tester", name);
assert_eq!(1985, birth_year);
}
#[test]
fn vec_tuple_extraction_test() {
let res: Vec<(String, u32)> = fake_vec_query("foo").unwrap();
assert_eq!(("tester".to_string(), 1985), res[0]);
assert_eq!(("test2".to_string(), 1986), res[1]);
}
fn fake_vec_query<T: FromTable>(_query: &str) -> GraphResult<T> {
query_with_statistics2().map(|(value, _)| value)
}
fn fake_query<T: FromTable>(_query: &str) -> GraphResult<T> {
query_with_statistics().map(|(value, _)| value)
}
fn query_with_statistics<T: FromTable>() -> GraphResult<(T, Statistics)> {
let result_set = get_result_set()?;
let value = T::from_table(&result_set)?;
Ok((value, result_set.statistics))
}
fn query_with_statistics2<T: FromTable>() -> GraphResult<(T, Statistics)> {
let result_set = get_result_set2()?;
let value = T::from_table(&result_set)?;
Ok((value, result_set.statistics))
}
fn get_result_set() -> GraphResult<ResultSet> {
Ok(ResultSet {
statistics: Statistics(vec![]),
columns: vec![
Column::Scalars(vec![Scalar::String(GraphString::from(
"tester".to_string(),
))]),
Column::Scalars(vec![Scalar::Integer(1985)]),
],
})
}
fn get_result_set2() -> GraphResult<ResultSet> {
Ok(ResultSet {
statistics: Statistics(vec![]),
columns: vec![
Column::Scalars(vec![
Scalar::String(GraphString::from("tester".to_string())),
Scalar::String(GraphString::from("test2".to_string())),
]),
Column::Scalars(vec![Scalar::Integer(1985), Scalar::Integer(1986)]),
],
})
}
}