use crate::graph::GraphStorage;
use crate::*;
use std::sync::Arc;
pub type SideEffectFn<S> = Box<dyn Fn(&TraverserValue<S>, u64) + Send + Sync>;
type SideEffectFnArc<S> = Arc<dyn Fn(&TraverserValue<S>, u64) + Send + Sync>;
struct StepSideEffect<S: GraphStorage> {
func: SideEffectFnArc<S>,
}
impl<S: GraphStorage> Clone for StepSideEffect<S> {
fn clone(&self) -> Self {
Self {
func: self.func.clone(),
}
}
}
impl<S: GraphStorage> fmt::Display for StepSideEffect<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "side_effect(fn)")
}
}
impl<'g, S: GraphStorage + 'g> GraphTraversalSource<'g, S> {
pub fn side_effect(self, func: SideEffectFn<S>) -> Self {
self.push_step(GraphStep::SideEffect(
"side_effect",
Box::new(StepSideEffect {
func: Arc::from(func),
}),
))
}
}
impl __ {
pub fn side_effect<'g, S: GraphStorage + 'g>(
func: SideEffectFn<S>,
) -> AnonymousTraversal<'g, S> {
AnonymousTraversal::new().side_effect(func)
}
}
impl<'g, S: GraphStorage + 'g> AnonymousTraversal<'g, S> {
pub fn side_effect(self, func: SideEffectFn<S>) -> Self {
let step = GraphStep::SideEffect(
"side_effect",
Box::new(StepSideEffect {
func: Arc::from(func),
}),
);
self.push_step(step)
}
}
impl<'g, S: GraphStorage + 'g> GraphStepSideEffect<'g, S> for StepSideEffect<S> {
#[inline]
fn process(
&self,
_context: &mut TraverserContext<'_, S>,
source: &Traverser<S>,
) -> TraverserResult<()> {
(self.func)(&source.value, source.bulk);
Ok(())
}
fn clone_box(&self) -> Box<dyn GraphStepSideEffect<'g, S> + 'g> {
Box::new(self.clone())
}
}
struct StepSideEffectTraversal<'g, S: GraphStorage + 'g> {
child: AnonymousTraversal<'g, S>,
}
impl<'g, S: GraphStorage + 'g> Clone for StepSideEffectTraversal<'g, S> {
fn clone(&self) -> Self {
Self {
child: self.child.clone(),
}
}
}
impl<'g, S: GraphStorage + 'g> fmt::Display for StepSideEffectTraversal<'g, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "side_effect({})", self.child)
}
}
impl<'g, S: GraphStorage + 'g> GraphTraversalSource<'g, S> {
pub fn side_effect_t(self, child: AnonymousTraversal<'g, S>) -> Self {
self.push_step(GraphStep::SideEffect(
"side_effect",
Box::new(StepSideEffectTraversal { child }),
))
}
}
impl __ {
pub fn side_effect_t<'g, S: GraphStorage + 'g>(
child: AnonymousTraversal<'g, S>,
) -> AnonymousTraversal<'g, S> {
AnonymousTraversal::new().side_effect_t(child)
}
}
impl<'g, S: GraphStorage + 'g> AnonymousTraversal<'g, S> {
pub fn side_effect_t(self, child: AnonymousTraversal<'g, S>) -> Self {
self.push_step(GraphStep::SideEffect(
"side_effect",
Box::new(StepSideEffectTraversal { child }),
))
}
}
impl<'g, S: GraphStorage + 'g> GraphStepSideEffect<'g, S> for StepSideEffectTraversal<'g, S> {
fn process(
&self,
context: &mut TraverserContext<'_, S>,
source: &Traverser<S>,
) -> TraverserResult<()> {
self.child.apply_to(&mut *context, source)?;
Ok(())
}
fn child_traversals_mut(&mut self) -> Vec<&mut crate::traversal::AnonymousTraversal<'g, S>> {
vec![&mut self.child]
}
fn analyze_nested_paths(
&mut self,
analysis: &crate::traversal::optimizer::path_analysis::PathAnalysis,
_downstream: &crate::traversal::optimizer::path_analysis::PathRequirement,
) -> crate::traversal::optimizer::path_analysis::PathRequirement {
self.child.analyze_paths(
analysis,
crate::traversal::optimizer::path_analysis::PathRequirement::None,
)
}
fn clone_box(&self) -> Box<dyn GraphStepSideEffect<'g, S> + 'g> {
Box::new(self.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let result = g
.v(None)
.side_effect(Box::new(move |_, _| {}))
.count()
.next()
.unwrap()
.unwrap();
assert_eq!(result.as_i64(), Some(6));
}
#[test]
fn where_first() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let result = g
.v(None)
.where_t(__::side_effect(Box::new(move |_, _| {})))
.count()
.next()
.unwrap()
.unwrap();
assert_eq!(result.as_i64(), Some(6));
}
#[test]
fn where_second() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let result = g
.v(None)
.where_t(__::both(None).side_effect(Box::new(move |_, _| {})))
.count()
.next()
.unwrap()
.unwrap();
assert_eq!(result.as_i64(), Some(6));
}
#[test]
fn profile() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let result = g
.v(None)
.side_effect(Box::new(move |_, _| {}))
.count()
.profile()
.unwrap();
assert!(result.to_string().contains("side_effect("));
}
}