diff --git a/src/interpreter/filter.rs b/src/interpreter/filter.rs index 7618a69..8c0292d 100644 --- a/src/interpreter/filter.rs +++ b/src/interpreter/filter.rs @@ -9,6 +9,8 @@ use super::{value::Or, ElementContext, TryFromValue, Value}; pub use filter_proc_macro::{filter_fn, Args}; +type Structure<'doc> = BTreeMap, Value<'doc>>; + pub trait Args<'doc>: Sized { fn try_deserialize<'ast>(args: BTreeMap<&'ast str, Value<'doc>>) -> anyhow::Result; } @@ -94,10 +96,7 @@ fn attrs<'doc>(value: scraper::ElementRef<'doc>) -> anyhow::Result> } #[filter_fn] -fn take<'doc>( - mut value: BTreeMap, Value<'doc>>, - key: Arc, -) -> anyhow::Result> { +fn take<'doc>(mut value: Structure<'doc>, key: Arc) -> anyhow::Result> { Ok(value.remove(&key).unwrap_or(Value::Null)) } @@ -127,6 +126,59 @@ fn float<'doc>(value: Or>>) -> anyhow::Result> Ok(Value::Float(x)) } +#[filter_fn] +fn nth<'doc>(value: Vec>, i: i64) -> anyhow::Result> { + let i = match i { + ..=-1 => value.len() - i.unsigned_abs() as usize, + _ => i as usize, + }; + + match value.into_iter().nth(i) { + Some(x) => Ok(x), + None => anyhow::bail!(""), + } +} + +#[filter_fn] +fn keys<'doc>(value: Structure<'doc>) -> anyhow::Result> { + Ok(Value::List(value.into_keys().map(Value::String).collect())) +} + +#[filter_fn] +fn values<'doc>(value: Structure<'doc>) -> anyhow::Result> { + Ok(Value::List(value.into_values().collect())) +} + +// TODO: this is quite wasteful +#[filter_fn] +fn entries<'doc>(value: Structure<'doc>) -> anyhow::Result> { + Ok(Value::List( + value + .into_iter() + .map(|(k, v)| Value::List(vec![Value::String(k), v])) + .collect(), + )) +} + +#[filter_fn] +fn from_entries<'doc>(value: Vec>) -> anyhow::Result> { + value + .into_iter() + .map(|x| { + let tuple: Vec<_> = x.try_into()?; + + let [k, v] = tuple + .try_into() + .map_err(|_| anyhow::anyhow!("Expected a `List([key, value])`"))?; + + let k: Arc = k.try_into()?; + + Ok((k, v)) + }) + .collect::>() + .map(Value::Structure) +} + macro_rules! build_map { ($( $id: ident, @@ -148,6 +200,11 @@ static BUILTIN_FILTERS: LazyLock