use super::{
BasicTraversal, Step, StepResult, Traversal, Traverser, TraverserRequirement, TraverserValue,
};
use std::any::Any;
pub trait BranchStep: Step {
fn branches(&self) -> Vec<&dyn Traversal>;
}
#[derive(Debug, Clone)]
pub struct ChooseStep {
id: String,
labels: Vec<String>,
condition: Option<BasicTraversal>,
options: Vec<(TraverserValue, BasicTraversal)>,
default_option: Option<BasicTraversal>,
}
impl ChooseStep {
pub fn new() -> Self {
Self {
id: "choose_0".to_string(),
labels: Vec::new(),
condition: None,
options: Vec::new(),
default_option: None,
}
}
pub fn with_condition(mut self, condition: BasicTraversal) -> Self {
self.condition = Some(condition);
self
}
pub fn option(mut self, value: TraverserValue, traversal: BasicTraversal) -> Self {
self.options.push((value, traversal));
self
}
pub fn default(mut self, traversal: BasicTraversal) -> Self {
self.default_option = Some(traversal);
self
}
}
impl Default for ChooseStep {
fn default() -> Self {
Self::new()
}
}
impl Step for ChooseStep {
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
"ChooseStep"
}
fn labels(&self) -> &[String] {
&self.labels
}
fn add_label(&mut self, label: String) {
if !self.labels.contains(&label) {
self.labels.push(label);
}
}
fn requirements(&self) -> &[TraverserRequirement] {
&[]
}
fn process_traverser(&self, traverser: Traverser) -> StepResult {
if self.default_option.is_some() {
StepResult::emit_one(traverser)
} else {
StepResult::Filter
}
}
fn reset(&mut self) {}
fn clone_step(&self) -> Box<dyn Step> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Debug, Clone)]
pub struct UnionStep {
id: String,
labels: Vec<String>,
branches: Vec<BasicTraversal>,
is_start: bool,
}
impl UnionStep {
pub fn new(branches: Vec<BasicTraversal>) -> Self {
Self {
id: format!("union_{}", branches.len()),
labels: Vec::new(),
branches,
is_start: false,
}
}
pub fn as_start(mut self) -> Self {
self.is_start = true;
self
}
pub fn get_branches(&self) -> &[BasicTraversal] {
&self.branches
}
}
impl Step for UnionStep {
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
"UnionStep"
}
fn labels(&self) -> &[String] {
&self.labels
}
fn add_label(&mut self, label: String) {
if !self.labels.contains(&label) {
self.labels.push(label);
}
}
fn requirements(&self) -> &[TraverserRequirement] {
&[]
}
fn process_traverser(&self, traverser: Traverser) -> StepResult {
let results: Vec<Traverser> = self.branches.iter().map(|_| traverser.split()).collect();
StepResult::emit_many(results)
}
fn reset(&mut self) {
for branch in &mut self.branches {
branch.reset();
}
}
fn clone_step(&self) -> Box<dyn Step> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Debug, Clone)]
pub struct CoalesceStep {
id: String,
labels: Vec<String>,
branches: Vec<BasicTraversal>,
}
impl CoalesceStep {
pub fn new(branches: Vec<BasicTraversal>) -> Self {
Self {
id: format!("coalesce_{}", branches.len()),
labels: Vec::new(),
branches,
}
}
}
impl Step for CoalesceStep {
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
"CoalesceStep"
}
fn labels(&self) -> &[String] {
&self.labels
}
fn add_label(&mut self, label: String) {
if !self.labels.contains(&label) {
self.labels.push(label);
}
}
fn requirements(&self) -> &[TraverserRequirement] {
&[]
}
fn process_traverser(&self, traverser: Traverser) -> StepResult {
if !self.branches.is_empty() {
StepResult::emit_one(traverser)
} else {
StepResult::Filter
}
}
fn reset(&mut self) {
for branch in &mut self.branches {
branch.reset();
}
}
fn clone_step(&self) -> Box<dyn Step> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Debug, Clone)]
pub struct OptionalStep {
id: String,
labels: Vec<String>,
traversal: BasicTraversal,
}
impl OptionalStep {
pub fn new(traversal: BasicTraversal) -> Self {
Self {
id: "optional_0".to_string(),
labels: Vec::new(),
traversal,
}
}
}
impl Step for OptionalStep {
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
"OptionalStep"
}
fn labels(&self) -> &[String] {
&self.labels
}
fn add_label(&mut self, label: String) {
if !self.labels.contains(&label) {
self.labels.push(label);
}
}
fn requirements(&self) -> &[TraverserRequirement] {
&[]
}
fn process_traverser(&self, traverser: Traverser) -> StepResult {
StepResult::emit_one(traverser)
}
fn reset(&mut self) {
self.traversal.reset();
}
fn clone_step(&self) -> Box<dyn Step> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Debug, Clone)]
pub struct RepeatStep {
id: String,
labels: Vec<String>,
loop_name: String,
repeat_traversal: BasicTraversal,
until_traversal: Option<BasicTraversal>,
emit_traversal: Option<BasicTraversal>,
times: Option<u32>,
until_first: bool,
emit_first: bool,
}
impl RepeatStep {
pub fn new(repeat_traversal: BasicTraversal) -> Self {
static COUNTER: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
let id = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Self {
id: format!("repeat_{}", id),
labels: Vec::new(),
loop_name: format!("repeat_{}", id),
repeat_traversal,
until_traversal: None,
emit_traversal: None,
times: None,
until_first: false,
emit_first: false,
}
}
pub fn with_name(mut self, name: String) -> Self {
self.loop_name = name;
self
}
pub fn until(mut self, traversal: BasicTraversal) -> Self {
self.until_traversal = Some(traversal);
self
}
pub fn emit(mut self, traversal: BasicTraversal) -> Self {
self.emit_traversal = Some(traversal);
self
}
pub fn times(mut self, times: u32) -> Self {
self.times = Some(times);
self
}
pub fn until_first(mut self) -> Self {
self.until_first = true;
self
}
pub fn emit_first(mut self) -> Self {
self.emit_first = true;
self
}
pub fn loop_name(&self) -> &str {
&self.loop_name
}
}
impl Step for RepeatStep {
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
"RepeatStep"
}
fn labels(&self) -> &[String] {
&self.labels
}
fn add_label(&mut self, label: String) {
if !self.labels.contains(&label) {
self.labels.push(label);
}
}
fn requirements(&self) -> &[TraverserRequirement] {
static REQS: &[TraverserRequirement] =
&[TraverserRequirement::SingleLoop, TraverserRequirement::Path];
REQS
}
fn process_traverser(&self, mut traverser: Traverser) -> StepResult {
traverser.init_loop(&self.loop_name);
if let Some(times) = self.times {
if traverser.loop_count(&self.loop_name) >= times {
return StepResult::emit_one(traverser);
}
}
traverser.incr_loop(&self.loop_name);
StepResult::emit_one(traverser)
}
fn reset(&mut self) {
self.repeat_traversal.reset();
if let Some(ref mut t) = self.until_traversal {
t.reset();
}
if let Some(ref mut t) = self.emit_traversal {
t.reset();
}
}
fn clone_step(&self) -> Box<dyn Step> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Debug, Clone)]
pub struct LocalStep {
id: String,
labels: Vec<String>,
traversal: BasicTraversal,
}
impl LocalStep {
pub fn new(traversal: BasicTraversal) -> Self {
Self {
id: "local_0".to_string(),
labels: Vec::new(),
traversal,
}
}
}
impl Step for LocalStep {
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
"LocalStep"
}
fn labels(&self) -> &[String] {
&self.labels
}
fn add_label(&mut self, label: String) {
if !self.labels.contains(&label) {
self.labels.push(label);
}
}
fn requirements(&self) -> &[TraverserRequirement] {
&[]
}
fn process_traverser(&self, traverser: Traverser) -> StepResult {
StepResult::emit_one(traverser)
}
fn reset(&mut self) {
self.traversal.reset();
}
fn clone_step(&self) -> Box<dyn Step> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_choose_step() {
let step = ChooseStep::new();
assert_eq!(step.name(), "ChooseStep");
}
#[test]
fn test_union_step() {
let branch1 = BasicTraversal::new();
let branch2 = BasicTraversal::new();
let step = UnionStep::new(vec![branch1, branch2]);
assert_eq!(step.get_branches().len(), 2);
let traverser = Traverser::new("v1");
let result = step.process_traverser(traverser);
if let StepResult::Emit(traversers) = result {
assert_eq!(traversers.len(), 2);
}
}
#[test]
fn test_coalesce_step() {
let step = CoalesceStep::new(vec![BasicTraversal::new()]);
assert_eq!(step.name(), "CoalesceStep");
let traverser = Traverser::new("v1");
let result = step.process_traverser(traverser);
assert!(matches!(result, StepResult::Emit(_)));
}
#[test]
fn test_optional_step() {
let step = OptionalStep::new(BasicTraversal::new());
assert_eq!(step.name(), "OptionalStep");
let traverser = Traverser::new("v1");
let result = step.process_traverser(traverser);
assert!(matches!(result, StepResult::Emit(_)));
}
#[test]
fn test_repeat_step() {
let repeat_t = BasicTraversal::new();
let step = RepeatStep::new(repeat_t).times(3);
assert_eq!(step.name(), "RepeatStep");
let traverser = Traverser::new("v1");
let result = step.process_traverser(traverser);
if let StepResult::Emit(t) = result {
assert_eq!(t[0].loop_count(step.loop_name()), 1);
}
}
#[test]
fn test_repeat_with_until_first() {
let step = RepeatStep::new(BasicTraversal::new())
.until_first()
.times(5);
assert!(step.until_first);
}
#[test]
fn test_local_step() {
let step = LocalStep::new(BasicTraversal::new());
assert_eq!(step.name(), "LocalStep");
}
}