use crate::json;
use crate::serde_json::Value;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone)]
pub enum TraverserValue {
Vertex(String),
Edge {
id: String,
source: String,
target: String,
label: String,
},
Property(String, Value),
Path(Path),
Map(HashMap<String, Value>),
List(Vec<Value>),
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Null,
}
impl TraverserValue {
pub fn as_vertex_id(&self) -> Option<&str> {
match self {
TraverserValue::Vertex(id) => Some(id),
TraverserValue::String(s) => Some(s),
_ => None,
}
}
pub fn as_edge(&self) -> Option<(&str, &str, &str, &str)> {
match self {
TraverserValue::Edge {
id,
source,
target,
label,
} => Some((id, source, target, label)),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, TraverserValue::Null)
}
pub fn to_json(&self) -> Value {
match self {
TraverserValue::Vertex(id) => json!({ "type": "vertex", "id": id }),
TraverserValue::Edge {
id,
source,
target,
label,
} => {
json!({
"type": "edge",
"id": id,
"source": source,
"target": target,
"label": label
})
}
TraverserValue::Property(key, value) => {
json!({ "key": key, "value": value })
}
TraverserValue::Path(path) => path.to_json(),
TraverserValue::Map(map) => {
Value::Object(map.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
}
TraverserValue::List(list) => Value::Array(list.clone()),
TraverserValue::String(s) => Value::String(s.clone()),
TraverserValue::Integer(i) => json!(i),
TraverserValue::Float(f) => json!(f),
TraverserValue::Boolean(b) => Value::Bool(*b),
TraverserValue::Null => Value::Null,
}
}
}
impl PartialEq for TraverserValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(TraverserValue::Vertex(a), TraverserValue::Vertex(b)) => a == b,
(TraverserValue::String(a), TraverserValue::String(b)) => a == b,
(TraverserValue::Integer(a), TraverserValue::Integer(b)) => a == b,
(TraverserValue::Boolean(a), TraverserValue::Boolean(b)) => a == b,
(TraverserValue::Null, TraverserValue::Null) => true,
_ => false,
}
}
}
impl Eq for TraverserValue {}
impl Hash for TraverserValue {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
TraverserValue::Vertex(id) => {
0u8.hash(state);
id.hash(state);
}
TraverserValue::String(s) => {
1u8.hash(state);
s.hash(state);
}
TraverserValue::Integer(i) => {
2u8.hash(state);
i.hash(state);
}
TraverserValue::Boolean(b) => {
3u8.hash(state);
b.hash(state);
}
TraverserValue::Null => {
4u8.hash(state);
}
_ => {
255u8.hash(state);
format!("{:?}", self).hash(state);
}
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Path {
objects: Vec<TraverserValue>,
labels: Vec<Vec<String>>,
}
impl Path {
pub fn new() -> Self {
Self::default()
}
pub fn start(value: TraverserValue) -> Self {
Self {
objects: vec![value],
labels: vec![vec![]],
}
}
pub fn extend(&mut self, value: TraverserValue) {
self.objects.push(value);
self.labels.push(vec![]);
}
pub fn extend_with_label(&mut self, value: TraverserValue, label: String) {
self.objects.push(value);
self.labels.push(vec![label]);
}
pub fn add_label(&mut self, label: String) {
if let Some(last_labels) = self.labels.last_mut() {
if !last_labels.contains(&label) {
last_labels.push(label);
}
}
}
pub fn objects(&self) -> &[TraverserValue] {
&self.objects
}
pub fn labels_at(&self, index: usize) -> &[String] {
self.labels.get(index).map(|v| v.as_slice()).unwrap_or(&[])
}
pub fn get(&self, label: &str) -> Option<&TraverserValue> {
for (i, labels) in self.labels.iter().enumerate() {
if labels.contains(&label.to_string()) {
return self.objects.get(i);
}
}
None
}
pub fn get_all(&self, label: &str) -> Vec<&TraverserValue> {
let mut result = Vec::new();
for (i, labels) in self.labels.iter().enumerate() {
if labels.contains(&label.to_string()) {
if let Some(obj) = self.objects.get(i) {
result.push(obj);
}
}
}
result
}
pub fn has_label(&self, label: &str) -> bool {
self.labels
.iter()
.any(|labels| labels.contains(&label.to_string()))
}
pub fn len(&self) -> usize {
self.objects.len()
}
pub fn is_empty(&self) -> bool {
self.objects.is_empty()
}
pub fn head(&self) -> Option<&TraverserValue> {
self.objects.last()
}
pub fn clone_and_extend(&self, value: TraverserValue) -> Self {
let mut new_path = self.clone();
new_path.extend(value);
new_path
}
pub fn to_json(&self) -> Value {
let objects: Vec<_> = self.objects.iter().map(|o| o.to_json()).collect();
let labels: Vec<_> = self
.labels
.iter()
.map(|l| Value::Array(l.iter().map(|s| Value::String(s.clone())).collect()))
.collect();
json!({
"objects": objects,
"labels": labels
})
}
pub fn retract(&mut self, keep_labels: &[String]) {
let mut new_objects = Vec::new();
let mut new_labels = Vec::new();
for (i, labels) in self.labels.iter().enumerate() {
if labels.iter().any(|l| keep_labels.contains(l)) {
if let Some(obj) = self.objects.get(i) {
new_objects.push(obj.clone());
new_labels.push(labels.clone());
}
}
}
self.objects = new_objects;
self.labels = new_labels;
}
}
#[derive(Debug, Clone, Default)]
pub struct LoopState {
loops: HashMap<String, u32>,
current_loop: Option<String>,
}
impl LoopState {
pub fn new() -> Self {
Self::default()
}
pub fn init_loop(&mut self, name: &str) {
self.loops.insert(name.to_string(), 0);
self.current_loop = Some(name.to_string());
}
pub fn incr_loop(&mut self, name: &str) {
if let Some(count) = self.loops.get_mut(name) {
*count += 1;
}
}
pub fn loop_count(&self, name: &str) -> u32 {
self.loops.get(name).copied().unwrap_or(0)
}
pub fn current_count(&self) -> u32 {
self.current_loop
.as_ref()
.and_then(|name| self.loops.get(name))
.copied()
.unwrap_or(0)
}
pub fn reset_loop(&mut self, name: &str) {
self.loops.remove(name);
if self.current_loop.as_deref() == Some(name) {
self.current_loop = None;
}
}
}
#[derive(Debug, Clone)]
pub struct Traverser {
value: TraverserValue,
bulk: u64,
path: Option<Path>,
loops: Option<LoopState>,
sack: Option<Value>,
tags: HashMap<String, String>,
}
impl Traverser {
pub fn new(vertex_id: &str) -> Self {
Self {
value: TraverserValue::Vertex(vertex_id.to_string()),
bulk: 1,
path: None,
loops: None,
sack: None,
tags: HashMap::new(),
}
}
pub fn with_value(value: TraverserValue) -> Self {
Self {
value,
bulk: 1,
path: None,
loops: None,
sack: None,
tags: HashMap::new(),
}
}
pub fn value(&self) -> &TraverserValue {
&self.value
}
pub fn set_value(&mut self, value: TraverserValue) {
self.value = value;
}
pub fn bulk(&self) -> u64 {
self.bulk
}
pub fn set_bulk(&mut self, bulk: u64) {
self.bulk = bulk;
}
pub fn multiply_bulk(&mut self, factor: u64) {
self.bulk *= factor;
}
pub fn path(&self) -> Option<&Path> {
self.path.as_ref()
}
pub fn path_mut(&mut self) -> Option<&mut Path> {
self.path.as_mut()
}
pub fn enable_path(&mut self) {
if self.path.is_none() {
self.path = Some(Path::start(self.value.clone()));
}
}
pub fn extend_path(&mut self, value: TraverserValue) {
if let Some(ref mut path) = self.path {
path.extend(value);
}
}
pub fn add_path_label(&mut self, label: String) {
if let Some(ref mut path) = self.path {
path.add_label(label);
}
}
pub fn loops(&self) -> Option<&LoopState> {
self.loops.as_ref()
}
pub fn loops_mut(&mut self) -> Option<&mut LoopState> {
self.loops.as_mut()
}
pub fn enable_loops(&mut self) {
if self.loops.is_none() {
self.loops = Some(LoopState::new());
}
}
pub fn init_loop(&mut self, name: &str) {
self.enable_loops();
if let Some(ref mut loops) = self.loops {
loops.init_loop(name);
}
}
pub fn incr_loop(&mut self, name: &str) {
if let Some(ref mut loops) = self.loops {
loops.incr_loop(name);
}
}
pub fn loop_count(&self, name: &str) -> u32 {
self.loops.as_ref().map(|l| l.loop_count(name)).unwrap_or(0)
}
pub fn sack(&self) -> Option<&Value> {
self.sack.as_ref()
}
pub fn set_sack(&mut self, value: Value) {
self.sack = Some(value);
}
pub fn tag(&self, key: &str) -> Option<&String> {
self.tags.get(key)
}
pub fn set_tag(&mut self, key: String, value: String) {
self.tags.insert(key, value);
}
pub fn split(&self) -> Self {
Self {
value: self.value.clone(),
bulk: 1, path: self.path.clone(),
loops: self.loops.clone(),
sack: self.sack.clone(),
tags: self.tags.clone(),
}
}
pub fn clone_with_value(&self, value: TraverserValue) -> Self {
let mut new = self.clone();
new.value = value;
new
}
pub fn merge(&mut self, other: &Traverser) {
self.bulk += other.bulk;
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TraverserRequirement {
Bulk,
Path,
SingleLoop,
NestedLoop,
Labels,
Sack,
Barrier,
Mutates,
}
pub struct TraverserGenerator {
requirements: Vec<TraverserRequirement>,
}
impl TraverserGenerator {
pub fn new(requirements: Vec<TraverserRequirement>) -> Self {
Self { requirements }
}
pub fn generate(&self, vertex_id: &str) -> Traverser {
let mut traverser = Traverser::new(vertex_id);
self.apply_requirements(&mut traverser);
traverser
}
pub fn generate_value(&self, value: TraverserValue) -> Traverser {
let mut traverser = Traverser::with_value(value);
self.apply_requirements(&mut traverser);
traverser
}
pub fn generate_many(&self, vertex_ids: &[String]) -> Vec<Traverser> {
vertex_ids.iter().map(|id| self.generate(id)).collect()
}
fn apply_requirements(&self, traverser: &mut Traverser) {
for req in &self.requirements {
match req {
TraverserRequirement::Path => traverser.enable_path(),
TraverserRequirement::SingleLoop | TraverserRequirement::NestedLoop => {
traverser.enable_loops()
}
_ => {}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_traverser_new() {
let t = Traverser::new("v1");
assert!(matches!(t.value(), TraverserValue::Vertex(id) if id == "v1"));
assert_eq!(t.bulk(), 1);
}
#[test]
fn test_traverser_path() {
let mut t = Traverser::new("v1");
assert!(t.path().is_none());
t.enable_path();
assert!(t.path().is_some());
assert_eq!(t.path().unwrap().len(), 1);
t.extend_path(TraverserValue::Vertex("v2".to_string()));
assert_eq!(t.path().unwrap().len(), 2);
}
#[test]
fn test_traverser_loops() {
let mut t = Traverser::new("v1");
t.init_loop("repeat_0");
assert_eq!(t.loop_count("repeat_0"), 0);
t.incr_loop("repeat_0");
assert_eq!(t.loop_count("repeat_0"), 1);
t.incr_loop("repeat_0");
assert_eq!(t.loop_count("repeat_0"), 2);
}
#[test]
fn test_traverser_split() {
let mut t = Traverser::new("v1");
t.set_bulk(5);
t.enable_path();
let split = t.split();
assert_eq!(split.bulk(), 1); assert!(split.path().is_some()); }
#[test]
fn test_path_labels() {
let mut path = Path::new();
path.extend_with_label(TraverserValue::Vertex("v1".to_string()), "a".to_string());
path.extend_with_label(TraverserValue::Vertex("v2".to_string()), "b".to_string());
assert!(path.has_label("a"));
assert!(path.has_label("b"));
assert!(!path.has_label("c"));
let v1 = path.get("a").unwrap();
assert!(matches!(v1, TraverserValue::Vertex(id) if id == "v1"));
}
#[test]
fn test_path_retract() {
let mut path = Path::new();
path.extend_with_label(TraverserValue::Vertex("v1".to_string()), "a".to_string());
path.extend(TraverserValue::Vertex("v2".to_string())); path.extend_with_label(TraverserValue::Vertex("v3".to_string()), "b".to_string());
path.retract(&["a".to_string(), "b".to_string()]);
assert_eq!(path.len(), 2); }
#[test]
fn test_traverser_generator() {
let gen = TraverserGenerator::new(vec![
TraverserRequirement::Path,
TraverserRequirement::SingleLoop,
]);
let t = gen.generate("v1");
assert!(t.path().is_some());
assert!(t.loops().is_some());
}
}