//! Per-line evaluation context.
//!
//! An [`EvalContext`] is a small string->string map populated by the
//! renderer pipeline before invoking the filter. Values are stored as
//! strings; the evaluator lazily coerces them to integers when the right-hand
//! side of the comparison is an integer literal.
use std::collections::HashMap;
#[derive(Debug, Default, Clone)]
pub struct EvalContext {
values: HashMap<String, String>,
}
impl EvalContext {
pub fn new() -> Self {
Self::default()
}
/// Set a field. Overwrites previous value.
pub fn set(&mut self, key: &str, value: impl Into<String>) {
self.values.insert(key.to_string(), value.into());
}
/// Bulk-load from an iterator of key/value pairs.
pub fn extend<I, K, V>(&mut self, iter: I)
where
I: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<String>,
{
for (k, v) in iter {
self.values.insert(k.into(), v.into());
}
}
/// Look up a field as a string. Returns the empty string if missing, so
/// comparisons against missing fields naturally fail rather than panic.
pub fn get(&self, key: &str) -> &str {
self.values
.get(key)
.map(|s| s.as_str())
.unwrap_or("")
}
/// Look up a field as an integer. Returns `None` if the field is missing
/// or not parseable.
pub fn get_int(&self, key: &str) -> Option<i64> {
self.values.get(key)?.parse::<i64>().ok()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn clear(&mut self) {
self.values.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.values.iter().map(|(k, v)| (k.as_str(), v.as_str()))
}
}