use pel::expression::{
Apply, Body, DefaultOperator, Expression, IfElse, Operation, Selection, Symbol, UnaryOperation,
};
use pel::runtime::value::Value;
use pel::runtime::{Binding, Context, ValueHandler};
use pel::Reference;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
#[derive(Default)]
pub struct TransientContext {
vars: HashMap<Reference, OwnedValueHandler>,
}
impl TransientContext {
pub fn detach_pending(&mut self, expression: &Expression, context: &dyn Context) {
for reference in expression.references() {
if let Entry::Vacant(entry) = self.vars.entry(reference) {
if let Some(value) = context
.value_handler(reference)
.and_then(ValueHandler::detach)
{
entry.insert(OwnedValueHandler::new(value));
}
}
}
}
}
impl Context for TransientContext {
fn resolve(&self, _symbol: &Symbol) -> Binding {
Binding::Unknown
}
fn value_handler(&self, reference: Reference) -> Option<&dyn ValueHandler> {
self.vars.get(&reference).map(|v| v as &dyn ValueHandler)
}
}
struct OwnedValueHandler {
value: Value,
}
impl OwnedValueHandler {
pub fn new(value: Value) -> Self {
Self { value }
}
}
impl ValueHandler for OwnedValueHandler {
fn detach(&self) -> Option<Value> {
Some(self.value.clone())
}
fn select_by_key(&self, key: &str) -> Option<Value> {
if let Some(obj) = self.value.as_object() {
return obj.get(key).cloned();
};
Some(Value::null())
}
fn select_by_index(&self, index: usize) -> Option<Value> {
if let Some(string) = self.value.as_str() {
return Some(
string
.chars()
.nth(index)
.map(|c| Value::string(c.to_string()))
.unwrap_or_else(Value::null),
);
};
if let Some(array) = self.value.as_slice() {
return Some(array.get(index).cloned().unwrap_or_else(Value::null));
};
if let Some(obj) = self.value.as_object() {
return Some(obj.values().nth(index).cloned().unwrap_or_else(Value::null));
};
Some(Value::null())
}
fn size(&self) -> Option<usize> {
if let Some(string) = self.value.as_str() {
return Some(string.len());
};
if let Some(array) = self.value.as_slice() {
return Some(array.len());
};
if let Some(obj) = self.value.as_object() {
return Some(obj.len());
};
None
}
}
trait ReferenceCollector {
fn references(&self) -> Vec<Reference>;
}
impl ReferenceCollector for Value {
fn references(&self) -> Vec<Reference> {
self.as_reference().map(|e| vec![e]).unwrap_or_default()
}
}
impl ReferenceCollector for Selection {
fn references(&self) -> Vec<Reference> {
let mut result = Vec::new();
result.append(&mut self.target.references());
result.append(&mut self.selector.references());
result
}
}
impl ReferenceCollector for Vec<Expression> {
fn references(&self) -> Vec<Reference> {
let mut result = Vec::new();
self.iter()
.for_each(|exp| result.append(&mut exp.references()));
result
}
}
impl ReferenceCollector for Apply {
fn references(&self) -> Vec<Reference> {
let mut result = Vec::new();
result.append(&mut self.function.references());
result.append(&mut self.arguments.references());
result
}
}
impl ReferenceCollector for DefaultOperator {
fn references(&self) -> Vec<Reference> {
let mut result = Vec::new();
result.append(&mut self.left.references());
result.append(&mut self.right.references());
result
}
}
impl ReferenceCollector for IfElse {
fn references(&self) -> Vec<Reference> {
let mut result = Vec::new();
result.append(&mut self.condition.references());
result.append(&mut self.true_branch.references());
result.append(&mut self.false_branch.references());
result
}
}
impl ReferenceCollector for UnaryOperation {
fn references(&self) -> Vec<Reference> {
self.operand.references()
}
}
impl ReferenceCollector for Operation {
fn references(&self) -> Vec<Reference> {
let mut result = Vec::new();
result.append(&mut self.left.references());
result.append(&mut self.right.references());
result
}
}
impl ReferenceCollector for Expression {
fn references(&self) -> Vec<Reference> {
match &self.body {
Body::Value(value) => value.references(),
Body::Selection(selection) => selection.references(),
Body::Array(array) => array.references(),
Body::Apply(apply) => apply.references(),
Body::DefaultOperator(default) => default.references(),
Body::IfElse(ifelse) => ifelse.references(),
Body::UnaryOperation(unary) => unary.references(),
Body::Operation(operation) => operation.references(),
_ => vec![],
}
}
}