use std::collections::HashSet;
use crate::Node;
use crate::SetOperationNode;
use super::Visitor;
use super::error::InterpreterError;
use super::{Interpreter, InterpreterResult, SelectionResult};
use super::result::NodeHandle;
pub fn apply_set_operation(it: &mut Interpreter, node: &SetOperationNode) -> InterpreterResult<()> {
match node {
SetOperationNode::Union(left, right) => union_operation(it, left, right)?,
SetOperationNode::Intersection(left, right) => intersection_operation(it, left, right)?,
SetOperationNode::Difference(left, right) => difference_operation(it, left, right)?,
}
Ok(())
}
enum OperationResults {
Nodes(Vec<NodeHandle>, Vec<NodeHandle>),
Texts(Vec<String>, Vec<String>),
}
fn execute_sides(
it: &Interpreter,
left: &Box<Node>,
right: &Box<Node>,
op_name: &str,
) -> InterpreterResult<OperationResults> {
let mut it_left = it.clone();
let mut it_right = it.clone();
it_left.visit_node(left)?;
it_right.visit_node(right)?;
let left_results = it_left.result;
let right_results = it_right.result;
if left_results.is_nodes() != right_results.is_nodes() {
return Err(InterpreterError::execution_error(
&format!("{} operation has inconsistent result types: left side is {}, right side is {}",
op_name,
if left_results.is_nodes() { "nodes" } else { "texts" },
if right_results.is_nodes() { "nodes" } else { "texts" }
)
));
}
if left_results.is_nodes() {
let left_nodes = left_results.nodes()?.clone();
let right_nodes = right_results.nodes()?.clone();
Ok(OperationResults::Nodes(left_nodes, right_nodes))
} else {
let left_texts = left_results.texts()?.clone();
let right_texts = right_results.texts()?.clone();
Ok(OperationResults::Texts(left_texts, right_texts))
}
}
fn union_operation(
it: &mut Interpreter,
left: &Box<Node>,
right: &Box<Node>,
) -> InterpreterResult<()> {
match execute_sides(it, left, right, "union")? {
OperationResults::Nodes(left_nodes, right_nodes) => {
let estimated_size = left_nodes.len() + right_nodes.len();
let mut seen_ids = HashSet::with_capacity(estimated_size);
let mut result = Vec::with_capacity(estimated_size);
for node in left_nodes {
let id = node.id().to_string();
if seen_ids.insert(id) {
result.push(node);
}
}
for node in right_nodes {
let id = node.id().to_string();
if seen_ids.insert(id) {
result.push(node);
}
}
it.result = SelectionResult::with_nodes(result);
},
OperationResults::Texts(left_texts, right_texts) => {
let estimated_size = left_texts.len() + right_texts.len();
let mut seen_texts = HashSet::with_capacity(estimated_size);
let mut result = Vec::with_capacity(estimated_size);
for text in left_texts {
if seen_texts.insert(text.clone()) {
result.push(text);
}
}
for text in right_texts {
if seen_texts.insert(text.clone()) {
result.push(text);
}
}
it.result = SelectionResult::with_texts(result);
}
}
Ok(())
}
fn intersection_operation(
it: &mut Interpreter,
left: &Box<Node>,
right: &Box<Node>,
) -> InterpreterResult<()> {
match execute_sides(it, left, right, "intersection")? {
OperationResults::Nodes(left_nodes, right_nodes) => {
let left_ids: HashSet<String> = left_nodes
.iter()
.map(|node| node.id().to_string())
.collect();
let mut node_result = Vec::with_capacity(right_nodes.len());
for node in right_nodes {
let node_id = node.id().to_string();
if left_ids.contains(&node_id) {
node_result.push(node);
}
}
it.result = SelectionResult::with_nodes(node_result);
},
OperationResults::Texts(left_texts, right_texts) => {
let left_text_set: HashSet<String> = left_texts.into_iter().collect();
let mut text_result = Vec::with_capacity(right_texts.len());
for text in right_texts {
if left_text_set.contains(&text) {
text_result.push(text);
}
}
it.result = SelectionResult::with_texts(text_result);
}
}
Ok(())
}
fn difference_operation(
it: &mut Interpreter,
left: &Box<Node>,
right: &Box<Node>,
) -> InterpreterResult<()> {
match execute_sides(it, left, right, "difference")? {
OperationResults::Nodes(left_nodes, right_nodes) => {
let right_ids: HashSet<String> = right_nodes
.iter()
.map(|node| node.id().to_string())
.collect();
let mut node_result = Vec::with_capacity(left_nodes.len());
for node in left_nodes {
let node_id = node.id().to_string();
if !right_ids.contains(&node_id) {
node_result.push(node);
}
}
it.result = SelectionResult::with_nodes(node_result);
},
OperationResults::Texts(left_texts, right_texts) => {
let right_text_set: HashSet<String> = right_texts.into_iter().collect();
let mut text_result = Vec::with_capacity(left_texts.len());
for text in left_texts {
if !right_text_set.contains(&text) {
text_result.push(text);
}
}
it.result = SelectionResult::with_texts(text_result);
}
}
Ok(())
}