use crate::datatypes::values::Value;
use petgraph::graph::{EdgeIndex, NodeIndex};
use std::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct Bindings<V> {
entries: Vec<(String, V)>,
}
impl<V> Bindings<V> {
pub fn new() -> Self {
Bindings {
entries: Vec::new(),
}
}
pub fn with_capacity(cap: usize) -> Self {
Bindings {
entries: Vec::with_capacity(cap),
}
}
pub fn get(&self, key: &str) -> Option<&V> {
self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
pub fn get_mut(&mut self, key: &str) -> Option<&mut V> {
self.entries
.iter_mut()
.find(|(k, _)| k == key)
.map(|(_, v)| v)
}
pub fn insert(&mut self, key: String, val: V) {
if let Some(entry) = self.entries.iter_mut().find(|(k, _)| *k == key) {
entry.1 = val;
} else {
self.entries.push((key, val));
}
}
pub fn contains_key(&self, key: &str) -> bool {
self.entries.iter().any(|(k, _)| k == key)
}
pub fn keys(&self) -> impl Iterator<Item = &String> {
self.entries.iter().map(|(k, _)| k)
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &V)> {
self.entries.iter().map(|(k, v)| (k, v))
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn remove(&mut self, key: &str) -> Option<V> {
if let Some(pos) = self.entries.iter().position(|(k, _)| k == key) {
Some(self.entries.swap_remove(pos).1)
} else {
None
}
}
pub fn to_hashmap(&self) -> HashMap<String, V>
where
V: Clone,
{
self.entries
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
}
impl<V> IntoIterator for Bindings<V> {
type Item = (String, V);
type IntoIter = std::vec::IntoIter<(String, V)>;
fn into_iter(self) -> Self::IntoIter {
self.entries.into_iter()
}
}
impl<'a, V> IntoIterator for &'a Bindings<V> {
type Item = &'a (String, V);
type IntoIter = std::slice::Iter<'a, (String, V)>;
fn into_iter(self) -> Self::IntoIter {
self.entries.iter()
}
}
#[derive(Debug, Clone)]
pub struct ResultRow {
pub node_bindings: Bindings<NodeIndex>,
pub edge_bindings: Bindings<EdgeBinding>,
pub path_bindings: Bindings<PathBinding>,
pub projected: Bindings<Value>,
}
#[derive(Debug, Clone, Copy)]
pub struct EdgeBinding {
pub source: NodeIndex,
pub target: NodeIndex,
pub edge_index: EdgeIndex,
}
#[derive(Debug, Clone)]
pub struct PathBinding {
pub source: NodeIndex,
pub hops: usize,
pub path: Vec<(NodeIndex, String)>,
}
impl ResultRow {
pub fn new() -> Self {
ResultRow {
node_bindings: Bindings::new(),
edge_bindings: Bindings::new(),
path_bindings: Bindings::new(),
projected: Bindings::new(),
}
}
pub fn with_capacity(nodes: usize, edges: usize, projected: usize) -> Self {
ResultRow {
node_bindings: Bindings::with_capacity(nodes),
edge_bindings: Bindings::with_capacity(edges),
path_bindings: Bindings::new(),
projected: Bindings::with_capacity(projected),
}
}
pub fn from_projected(projected: Bindings<Value>) -> Self {
ResultRow {
node_bindings: Bindings::new(),
edge_bindings: Bindings::new(),
path_bindings: Bindings::new(),
projected,
}
}
}
#[derive(Debug)]
pub struct ResultSet {
pub rows: Vec<ResultRow>,
pub columns: Vec<String>,
pub lazy_return_items: Option<Vec<super::ast::ReturnItem>>,
}
impl ResultSet {
pub fn new() -> Self {
ResultSet {
rows: Vec::new(),
columns: Vec::new(),
lazy_return_items: None,
}
}
}
#[derive(Debug, Clone)]
pub struct ClauseStats {
pub clause_name: String,
pub rows_in: usize,
pub rows_out: usize,
pub elapsed_us: u64,
}
#[derive(Debug, Clone, Default)]
pub struct MutationStats {
pub nodes_created: usize,
pub relationships_created: usize,
pub properties_set: usize,
pub nodes_deleted: usize,
pub relationships_deleted: usize,
pub properties_removed: usize,
}
#[derive(Debug, Clone, Default)]
pub struct QueryDiagnostics {
pub elapsed_ms: u64,
pub timed_out: bool,
pub timeout_ms: Option<u64>,
pub warnings: Vec<String>,
}
#[derive(Debug)]
pub struct LazyResultDescriptor {
pub pending_rows: Vec<ResultRow>,
pub return_items: Vec<super::ast::ReturnItem>,
}
#[derive(Debug)]
pub struct CypherResult {
pub columns: Vec<String>,
pub rows: Vec<Vec<Value>>,
pub stats: Option<MutationStats>,
pub profile: Option<Vec<ClauseStats>>,
pub diagnostics: Option<QueryDiagnostics>,
pub lazy: Option<LazyResultDescriptor>,
}
impl CypherResult {
pub fn empty() -> Self {
CypherResult {
columns: Vec::new(),
rows: Vec::new(),
stats: None,
profile: None,
diagnostics: None,
lazy: None,
}
}
pub fn to_csv(&self) -> String {
let mut buf = String::new();
for (i, col) in self.columns.iter().enumerate() {
if i > 0 {
buf.push(',');
}
csv_field(&mut buf, col);
}
buf.push('\n');
for row in &self.rows {
for (i, val) in row.iter().enumerate() {
if i > 0 {
buf.push(',');
}
csv_value(&mut buf, val);
}
buf.push('\n');
}
buf
}
}
fn csv_field(buf: &mut String, s: &str) {
if s.contains(',') || s.contains('"') || s.contains('\n') {
buf.push('"');
for c in s.chars() {
if c == '"' {
buf.push('"');
}
buf.push(c);
}
buf.push('"');
} else {
buf.push_str(s);
}
}
fn csv_value(buf: &mut String, val: &Value) {
match val {
Value::Null => {} Value::String(s) => csv_field(buf, s),
Value::Int64(n) => {
use std::fmt::Write;
let _ = write!(buf, "{}", n);
}
Value::Float64(f) => {
use std::fmt::Write;
let _ = write!(buf, "{}", f);
}
Value::Boolean(b) => buf.push_str(if *b { "true" } else { "false" }),
Value::UniqueId(u) => {
use std::fmt::Write;
let _ = write!(buf, "{}", u);
}
Value::DateTime(d) => buf.push_str(&d.format("%Y-%m-%d").to_string()),
Value::Point { lat, lon } => {
use std::fmt::Write;
let _ = write!(buf, "POINT({} {})", lon, lat);
}
Value::Duration {
months,
days,
seconds,
} => {
use std::fmt::Write;
let _ = write!(buf, "duration(M={}, D={}, S={})", months, days, seconds);
}
Value::NodeRef(idx) => {
use std::fmt::Write;
let _ = write!(buf, "{}", idx);
}
Value::List(_)
| Value::Map(_)
| Value::Node(_)
| Value::Relationship(_)
| Value::Path(_) => {
let s = crate::datatypes::values::format_value(val);
csv_field(buf, &s);
}
}
}