use std::collections::HashMap;
use crate::traversal::context::ExecutionContext;
use crate::traversal::step::{execute_traversal_from, Step};
use crate::traversal::{Traversal, Traverser};
use crate::value::Value;
#[derive(Clone, Debug, PartialEq)]
pub enum OptionKey {
Value(Value),
None,
}
impl OptionKey {
pub fn value<T: Into<Value>>(v: T) -> Self {
OptionKey::Value(v.into())
}
pub fn none() -> Self {
OptionKey::None
}
}
impl From<&str> for OptionKey {
fn from(s: &str) -> Self {
OptionKey::Value(Value::String(s.to_string()))
}
}
impl From<String> for OptionKey {
fn from(s: String) -> Self {
OptionKey::Value(Value::String(s))
}
}
impl From<i64> for OptionKey {
fn from(n: i64) -> Self {
OptionKey::Value(Value::Int(n))
}
}
impl From<i32> for OptionKey {
fn from(n: i32) -> Self {
OptionKey::Value(Value::Int(n as i64))
}
}
impl From<bool> for OptionKey {
fn from(b: bool) -> Self {
OptionKey::Value(Value::Bool(b))
}
}
impl From<Value> for OptionKey {
fn from(v: Value) -> Self {
OptionKey::Value(v)
}
}
#[derive(Clone, Debug)]
pub struct OptionKeyWrapper(pub OptionKey);
impl std::hash::Hash for OptionKeyWrapper {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match &self.0 {
OptionKey::Value(v) => {
0u8.hash(state);
v.hash(state);
}
OptionKey::None => {
1u8.hash(state);
}
}
}
}
impl PartialEq for OptionKeyWrapper {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for OptionKeyWrapper {}
#[derive(Clone)]
pub struct WhereStep {
sub: Traversal<Value, Value>,
}
impl WhereStep {
pub fn new(sub: Traversal<Value, Value>) -> Self {
Self { sub }
}
}
impl Step for WhereStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let sub = self.sub.clone();
input.filter(move |t| {
let sub_input = Box::new(std::iter::once(t.clone()));
let mut results = execute_traversal_from(ctx, &sub, sub_input);
results.next().is_some() })
}
fn name(&self) -> &'static str {
"where"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Filter
}
fn describe(&self) -> Option<String> {
use crate::traversal::explain::format_traversal_steps;
Some(format_traversal_steps(self.sub.steps()))
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
let mut results = execute_traversal_streaming(&ctx, &self.sub, input.clone());
if results.next().is_some() {
Box::new(std::iter::once(input))
} else {
Box::new(std::iter::empty())
}
}
}
#[derive(Clone)]
pub struct NotStep {
sub: Traversal<Value, Value>,
}
impl NotStep {
pub fn new(sub: Traversal<Value, Value>) -> Self {
Self { sub }
}
}
impl Step for NotStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let sub = self.sub.clone();
input.filter(move |t| {
let sub_input = Box::new(std::iter::once(t.clone()));
let mut results = execute_traversal_from(ctx, &sub, sub_input);
results.next().is_none() })
}
fn name(&self) -> &'static str {
"not"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Filter
}
fn describe(&self) -> Option<String> {
use crate::traversal::explain::format_traversal_steps;
Some(format_traversal_steps(self.sub.steps()))
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
let mut results = execute_traversal_streaming(&ctx, &self.sub, input.clone());
if results.next().is_none() {
Box::new(std::iter::once(input))
} else {
Box::new(std::iter::empty())
}
}
}
#[derive(Clone)]
pub struct WhereNeqStep {
label: String,
}
impl WhereNeqStep {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
}
}
}
impl Step for WhereNeqStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
_ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let label = self.label.clone();
input.filter(move |t| {
if let Some(labeled_values) = t.path.get(&label) {
if let Some(labeled_value) = labeled_values.first() {
return t.value != labeled_value.to_value();
}
}
true
})
}
fn name(&self) -> &'static str {
"where_neq"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Filter
}
fn apply_streaming(
&self,
_ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
if let Some(labeled_values) = input.path.get(&self.label) {
if let Some(labeled_value) = labeled_values.first() {
if input.value != labeled_value.to_value() {
return Box::new(std::iter::once(input));
} else {
return Box::new(std::iter::empty());
}
}
}
Box::new(std::iter::once(input))
}
}
#[derive(Clone)]
pub struct WhereEqStep {
label: String,
}
impl WhereEqStep {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
}
}
}
impl Step for WhereEqStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
_ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let label = self.label.clone();
input.filter(move |t| {
if let Some(labeled_values) = t.path.get(&label) {
if let Some(labeled_value) = labeled_values.first() {
return t.value == labeled_value.to_value();
}
}
false
})
}
fn name(&self) -> &'static str {
"where_eq"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Filter
}
fn apply_streaming(
&self,
_ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
if let Some(labeled_values) = input.path.get(&self.label) {
if let Some(labeled_value) = labeled_values.first() {
if input.value == labeled_value.to_value() {
return Box::new(std::iter::once(input));
}
}
}
Box::new(std::iter::empty())
}
}
#[derive(Clone)]
pub struct AndStep {
subs: Vec<Traversal<Value, Value>>,
}
impl AndStep {
pub fn new(subs: Vec<Traversal<Value, Value>>) -> Self {
Self { subs }
}
}
impl Step for AndStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let subs = self.subs.clone();
input.filter(move |t| {
subs.iter().all(|sub| {
let sub_input = Box::new(std::iter::once(t.clone()));
let mut results = execute_traversal_from(ctx, sub, sub_input);
results.next().is_some()
})
})
}
fn name(&self) -> &'static str {
"and"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Filter
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
for sub in &self.subs {
let mut results = execute_traversal_streaming(&ctx, sub, input.clone());
if results.next().is_none() {
return Box::new(std::iter::empty());
}
}
Box::new(std::iter::once(input))
}
}
#[derive(Clone)]
pub struct OrStep {
subs: Vec<Traversal<Value, Value>>,
}
impl OrStep {
pub fn new(subs: Vec<Traversal<Value, Value>>) -> Self {
Self { subs }
}
}
impl Step for OrStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let subs = self.subs.clone();
input.filter(move |t| {
subs.iter().any(|sub| {
let sub_input = Box::new(std::iter::once(t.clone()));
let mut results = execute_traversal_from(ctx, sub, sub_input);
results.next().is_some()
})
})
}
fn name(&self) -> &'static str {
"or"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Filter
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
for sub in &self.subs {
let mut results = execute_traversal_streaming(&ctx, sub, input.clone());
if results.next().is_some() {
return Box::new(std::iter::once(input));
}
}
Box::new(std::iter::empty())
}
}
#[derive(Clone)]
pub struct UnionStep {
branches: Vec<Traversal<Value, Value>>,
}
impl UnionStep {
pub fn new(branches: Vec<Traversal<Value, Value>>) -> Self {
Self { branches }
}
}
impl Step for UnionStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let branches = self.branches.clone();
input.flat_map(move |t| {
let mut results = Vec::new();
for branch in branches.iter() {
let sub_input = Box::new(std::iter::once(t.clone()));
results.extend(execute_traversal_from(ctx, branch, sub_input));
}
results.into_iter()
})
}
fn name(&self) -> &'static str {
"union"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Branch
}
fn describe(&self) -> Option<String> {
use crate::traversal::explain::format_traversal_steps;
let lines: Vec<String> = self
.branches
.iter()
.enumerate()
.map(|(i, b)| format!("branch {}: {}", i + 1, format_traversal_steps(b.steps())))
.collect();
Some(lines.join("\n"))
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
let mut results = Vec::new();
for branch in &self.branches {
results.extend(execute_traversal_streaming(&ctx, branch, input.clone()));
}
Box::new(results.into_iter())
}
}
#[derive(Clone)]
pub struct CoalesceStep {
branches: Vec<Traversal<Value, Value>>,
}
impl CoalesceStep {
pub fn new(branches: Vec<Traversal<Value, Value>>) -> Self {
Self { branches }
}
}
impl Step for CoalesceStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let branches = self.branches.clone();
input.flat_map(move |t| {
for branch in branches.iter() {
let sub_input = Box::new(std::iter::once(t.clone()));
let results: Vec<_> = execute_traversal_from(ctx, branch, sub_input).collect();
if !results.is_empty() {
return results.into_iter();
}
}
Vec::new().into_iter()
})
}
fn name(&self) -> &'static str {
"coalesce"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Branch
}
fn describe(&self) -> Option<String> {
use crate::traversal::explain::format_traversal_steps;
let lines: Vec<String> = self
.branches
.iter()
.enumerate()
.map(|(i, b)| format!("branch {}: {}", i + 1, format_traversal_steps(b.steps())))
.collect();
Some(lines.join("\n"))
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
for branch in &self.branches {
let results: Vec<_> =
execute_traversal_streaming(&ctx, branch, input.clone()).collect();
if !results.is_empty() {
return Box::new(results.into_iter());
}
}
Box::new(std::iter::empty())
}
}
#[derive(Clone)]
pub struct ChooseStep {
condition: Traversal<Value, Value>,
if_true: Traversal<Value, Value>,
if_false: Traversal<Value, Value>,
}
impl ChooseStep {
pub fn new(
condition: Traversal<Value, Value>,
if_true: Traversal<Value, Value>,
if_false: Traversal<Value, Value>,
) -> Self {
Self {
condition,
if_true,
if_false,
}
}
}
impl Step for ChooseStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let condition = self.condition.clone();
let if_true = self.if_true.clone();
let if_false = self.if_false.clone();
input.flat_map(move |t| {
let cond_input = Box::new(std::iter::once(t.clone()));
let mut cond_result = execute_traversal_from(ctx, &condition, cond_input);
let branch = if cond_result.next().is_some() {
&if_true
} else {
&if_false
};
let sub_input = Box::new(std::iter::once(t));
execute_traversal_from(ctx, branch, sub_input)
.collect::<Vec<_>>()
.into_iter()
})
}
fn name(&self) -> &'static str {
"choose"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Branch
}
fn describe(&self) -> Option<String> {
use crate::traversal::explain::format_traversal_steps;
Some(format!(
"if: {}\nthen: {}\nelse: {}",
format_traversal_steps(self.condition.steps()),
format_traversal_steps(self.if_true.steps()),
format_traversal_steps(self.if_false.steps()),
))
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
let mut cond_result = execute_traversal_streaming(&ctx, &self.condition, input.clone());
let branch = if cond_result.next().is_some() {
&self.if_true
} else {
&self.if_false
};
let results: Vec<_> = execute_traversal_streaming(&ctx, branch, input).collect();
Box::new(results.into_iter())
}
}
#[derive(Clone)]
pub struct OptionalStep {
sub: Traversal<Value, Value>,
}
impl OptionalStep {
pub fn new(sub: Traversal<Value, Value>) -> Self {
Self { sub }
}
}
impl Step for OptionalStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let sub = self.sub.clone();
input.flat_map(move |t| {
let sub_input = Box::new(std::iter::once(t.clone()));
let results: Vec<_> = execute_traversal_from(ctx, &sub, sub_input).collect();
if results.is_empty() {
vec![t].into_iter()
} else {
results.into_iter()
}
})
}
fn name(&self) -> &'static str {
"optional"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Branch
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
let results: Vec<_> = execute_traversal_streaming(&ctx, &self.sub, input.clone()).collect();
if results.is_empty() {
Box::new(std::iter::once(input))
} else {
Box::new(results.into_iter())
}
}
}
#[derive(Clone)]
pub struct LocalStep {
sub: Traversal<Value, Value>,
}
impl LocalStep {
pub fn new(sub: Traversal<Value, Value>) -> Self {
Self { sub }
}
}
impl Step for LocalStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let sub = self.sub.clone();
input.flat_map(move |t| {
let sub_input = Box::new(std::iter::once(t));
execute_traversal_from(ctx, &sub, sub_input)
.collect::<Vec<_>>()
.into_iter()
})
}
fn name(&self) -> &'static str {
"local"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Branch
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
let results: Vec<_> = execute_traversal_streaming(&ctx, &self.sub, input).collect();
Box::new(results.into_iter())
}
}
#[derive(Clone)]
pub struct BranchStep {
branch_traversal: Traversal<Value, Value>,
pub(crate) options: HashMap<OptionKeyWrapper, Traversal<Value, Value>>,
pub(crate) none_branch: Option<Traversal<Value, Value>>,
}
impl BranchStep {
pub fn new(branch_traversal: Traversal<Value, Value>) -> Self {
Self {
branch_traversal,
options: HashMap::new(),
none_branch: None,
}
}
pub fn add_option<K: Into<OptionKey>>(
mut self,
key: K,
branch: Traversal<Value, Value>,
) -> Self {
let key = key.into();
match key {
OptionKey::None => {
self.none_branch = Some(branch);
}
OptionKey::Value(_) => {
self.options.insert(OptionKeyWrapper(key), branch);
}
}
self
}
pub fn add_none_option(mut self, branch: Traversal<Value, Value>) -> Self {
self.none_branch = Some(branch);
self
}
}
impl Step for BranchStep {
type Iter<'a>
= impl Iterator<Item = Traverser> + 'a
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
let branch_traversal = self.branch_traversal.clone();
let options = self.options.clone();
let none_branch = self.none_branch.clone();
input.flat_map(move |t| {
let branch_input = Box::new(std::iter::once(t.clone()));
let mut branch_results = execute_traversal_from(ctx, &branch_traversal, branch_input);
let key_value = branch_results
.next()
.map(|key_traverser| key_traverser.value);
let branch = match key_value {
Some(key) => {
let option_key = OptionKeyWrapper(OptionKey::Value(key));
options.get(&option_key).or(none_branch.as_ref())
}
None => none_branch.as_ref(),
};
match branch {
Some(branch) => {
let sub_input = Box::new(std::iter::once(t));
execute_traversal_from(ctx, branch, sub_input)
.collect::<Vec<_>>()
.into_iter()
}
None => Vec::new().into_iter(),
}
})
}
fn name(&self) -> &'static str {
"branch"
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Branch
}
fn apply_streaming(
&self,
ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
use crate::traversal::step::execute_traversal_streaming;
let mut branch_results =
execute_traversal_streaming(&ctx, &self.branch_traversal, input.clone());
let key_value = branch_results.next().map(|t| t.value);
let branch = match key_value {
Some(key) => {
let option_key = OptionKeyWrapper(OptionKey::Value(key));
self.options.get(&option_key).or(self.none_branch.as_ref())
}
None => self.none_branch.as_ref(),
};
match branch {
Some(branch) => {
let results: Vec<_> = execute_traversal_streaming(&ctx, branch, input).collect();
Box::new(results.into_iter())
}
None => Box::new(std::iter::empty()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::traversal::step::DynStep;
#[test]
fn where_step_compiles() {
let sub = Traversal::<Value, Value>::new();
let step = WhereStep::new(sub);
assert_eq!(step.name(), "where");
}
#[test]
fn not_step_compiles() {
let sub = Traversal::<Value, Value>::new();
let step = NotStep::new(sub);
assert_eq!(step.name(), "not");
}
#[test]
fn and_step_compiles() {
let subs = vec![
Traversal::<Value, Value>::new(),
Traversal::<Value, Value>::new(),
];
let step = AndStep::new(subs);
assert_eq!(step.name(), "and");
}
#[test]
fn or_step_compiles() {
let subs = vec![
Traversal::<Value, Value>::new(),
Traversal::<Value, Value>::new(),
];
let step = OrStep::new(subs);
assert_eq!(step.name(), "or");
}
#[test]
fn union_step_compiles() {
let branches = vec![
Traversal::<Value, Value>::new(),
Traversal::<Value, Value>::new(),
];
let step = UnionStep::new(branches);
assert_eq!(step.name(), "union");
}
#[test]
fn coalesce_step_compiles() {
let branches = vec![
Traversal::<Value, Value>::new(),
Traversal::<Value, Value>::new(),
];
let step = CoalesceStep::new(branches);
assert_eq!(step.name(), "coalesce");
}
#[test]
fn choose_step_compiles() {
let condition = Traversal::<Value, Value>::new();
let if_true = Traversal::<Value, Value>::new();
let if_false = Traversal::<Value, Value>::new();
let step = ChooseStep::new(condition, if_true, if_false);
assert_eq!(step.name(), "choose");
}
#[test]
fn optional_step_compiles() {
let sub = Traversal::<Value, Value>::new();
let step = OptionalStep::new(sub);
assert_eq!(step.name(), "optional");
}
#[test]
fn local_step_compiles() {
let sub = Traversal::<Value, Value>::new();
let step = LocalStep::new(sub);
assert_eq!(step.name(), "local");
}
#[test]
fn steps_are_clonable() {
let sub = Traversal::<Value, Value>::new();
let where_step = WhereStep::new(sub.clone());
let _ = where_step.clone();
let not_step = NotStep::new(sub.clone());
let _ = not_step.clone();
let and_step = AndStep::new(vec![sub.clone()]);
let _ = and_step.clone();
let or_step = OrStep::new(vec![sub.clone()]);
let _ = or_step.clone();
let union_step = UnionStep::new(vec![sub.clone()]);
let _ = union_step.clone();
let coalesce_step = CoalesceStep::new(vec![sub.clone()]);
let _ = coalesce_step.clone();
let choose_step = ChooseStep::new(sub.clone(), sub.clone(), sub.clone());
let _ = choose_step.clone();
let optional_step = OptionalStep::new(sub.clone());
let _ = optional_step.clone();
let local_step = LocalStep::new(sub);
let _ = local_step.clone();
}
#[test]
fn steps_implement_any_step() {
let sub = Traversal::<Value, Value>::new();
let _: Box<dyn DynStep> = Box::new(WhereStep::new(sub.clone()));
let _: Box<dyn DynStep> = Box::new(NotStep::new(sub.clone()));
let _: Box<dyn DynStep> = Box::new(AndStep::new(vec![sub.clone()]));
let _: Box<dyn DynStep> = Box::new(OrStep::new(vec![sub.clone()]));
let _: Box<dyn DynStep> = Box::new(UnionStep::new(vec![sub.clone()]));
let _: Box<dyn DynStep> = Box::new(CoalesceStep::new(vec![sub.clone()]));
let _: Box<dyn DynStep> = Box::new(ChooseStep::new(sub.clone(), sub.clone(), sub.clone()));
let _: Box<dyn DynStep> = Box::new(OptionalStep::new(sub.clone()));
let _: Box<dyn DynStep> = Box::new(LocalStep::new(sub));
}
#[test]
fn option_key_value_from_string() {
let key = OptionKey::value("person");
assert_eq!(key, OptionKey::Value(Value::String("person".to_string())));
}
#[test]
fn option_key_value_from_integer() {
let key = OptionKey::value(42i64);
assert_eq!(key, OptionKey::Value(Value::Int(42)));
}
#[test]
fn option_key_value_from_bool() {
let key = OptionKey::value(true);
assert_eq!(key, OptionKey::Value(Value::Bool(true)));
}
#[test]
fn option_key_none_creates_none_variant() {
let key = OptionKey::none();
assert_eq!(key, OptionKey::None);
}
#[test]
fn option_key_from_str() {
let key: OptionKey = "person".into();
assert_eq!(key, OptionKey::Value(Value::String("person".to_string())));
}
#[test]
fn option_key_from_string() {
let key: OptionKey = String::from("software").into();
assert_eq!(key, OptionKey::Value(Value::String("software".to_string())));
}
#[test]
fn option_key_from_i64() {
let key: OptionKey = 42i64.into();
assert_eq!(key, OptionKey::Value(Value::Int(42)));
}
#[test]
fn option_key_from_i32() {
let key: OptionKey = 42i32.into();
assert_eq!(key, OptionKey::Value(Value::Int(42)));
}
#[test]
fn option_key_from_bool() {
let key: OptionKey = true.into();
assert_eq!(key, OptionKey::Value(Value::Bool(true)));
}
#[test]
fn option_key_from_value() {
let key: OptionKey = Value::Float(3.15).into();
assert_eq!(key, OptionKey::Value(Value::Float(3.15)));
}
#[test]
fn option_key_wrapper_hash_consistency() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn hash_key(key: &OptionKeyWrapper) -> u64 {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
hasher.finish()
}
let key1 = OptionKeyWrapper(OptionKey::from("person"));
let key2 = OptionKeyWrapper(OptionKey::from("person"));
assert_eq!(hash_key(&key1), hash_key(&key2));
let key3 = OptionKeyWrapper(OptionKey::from("software"));
assert_ne!(hash_key(&key1), hash_key(&key3));
let none1 = OptionKeyWrapper(OptionKey::None);
let none2 = OptionKeyWrapper(OptionKey::None);
assert_eq!(hash_key(&none1), hash_key(&none2));
assert_ne!(hash_key(&key1), hash_key(&none1));
}
#[test]
fn option_key_wrapper_equality() {
let key1 = OptionKeyWrapper(OptionKey::from("person"));
let key2 = OptionKeyWrapper(OptionKey::from("person"));
let key3 = OptionKeyWrapper(OptionKey::from("software"));
let none = OptionKeyWrapper(OptionKey::None);
assert_eq!(key1, key2);
assert_ne!(key1, key3);
assert_ne!(key1, none);
}
#[test]
fn option_key_wrapper_in_hashmap() {
let mut map: HashMap<OptionKeyWrapper, &str> = HashMap::new();
map.insert(OptionKeyWrapper(OptionKey::from("person")), "people branch");
map.insert(
OptionKeyWrapper(OptionKey::from("software")),
"software branch",
);
map.insert(OptionKeyWrapper(OptionKey::None), "default branch");
assert_eq!(
map.get(&OptionKeyWrapper(OptionKey::from("person"))),
Some(&"people branch")
);
assert_eq!(
map.get(&OptionKeyWrapper(OptionKey::from("software"))),
Some(&"software branch")
);
assert_eq!(
map.get(&OptionKeyWrapper(OptionKey::None)),
Some(&"default branch")
);
assert_eq!(map.get(&OptionKeyWrapper(OptionKey::from("unknown"))), None);
}
#[test]
fn branch_step_new_creates_empty_options() {
let branch_traversal = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal);
assert!(step.options.is_empty());
assert!(step.none_branch.is_none());
}
#[test]
fn branch_step_add_option_adds_to_map() {
let branch_traversal = Traversal::<Value, Value>::new();
let option_traversal = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal).add_option("person", option_traversal);
assert_eq!(step.options.len(), 1);
assert!(step
.options
.contains_key(&OptionKeyWrapper(OptionKey::from("person"))));
assert!(step.none_branch.is_none());
}
#[test]
fn branch_step_add_option_with_none_key_sets_none_branch() {
let branch_traversal = Traversal::<Value, Value>::new();
let option_traversal = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal).add_option(OptionKey::None, option_traversal);
assert!(step.options.is_empty());
assert!(step.none_branch.is_some());
}
#[test]
fn branch_step_add_none_option_sets_none_branch() {
let branch_traversal = Traversal::<Value, Value>::new();
let option_traversal = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal).add_none_option(option_traversal);
assert!(step.options.is_empty());
assert!(step.none_branch.is_some());
}
#[test]
fn branch_step_multiple_options() {
let branch_traversal = Traversal::<Value, Value>::new();
let option1 = Traversal::<Value, Value>::new();
let option2 = Traversal::<Value, Value>::new();
let default = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal)
.add_option("person", option1)
.add_option("software", option2)
.add_none_option(default);
assert_eq!(step.options.len(), 2);
assert!(step
.options
.contains_key(&OptionKeyWrapper(OptionKey::from("person"))));
assert!(step
.options
.contains_key(&OptionKeyWrapper(OptionKey::from("software"))));
assert!(step.none_branch.is_some());
}
#[test]
fn branch_step_compiles() {
let branch_traversal = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal);
assert_eq!(step.name(), "branch");
}
#[test]
fn branch_step_is_clonable() {
let branch_traversal = Traversal::<Value, Value>::new();
let option_traversal = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal)
.add_option("person", option_traversal.clone())
.add_none_option(option_traversal);
let cloned = step.clone();
assert_eq!(cloned.options.len(), step.options.len());
assert_eq!(cloned.none_branch.is_some(), step.none_branch.is_some());
}
#[test]
fn branch_step_implements_any_step() {
let branch_traversal = Traversal::<Value, Value>::new();
let step = BranchStep::new(branch_traversal);
let _: Box<dyn DynStep> = Box::new(step);
}
}