use super::{Node, VariableEval, VariableName};
use crate::error::EvalResult;
use a1_notation::{Address, RangeOrCell};
use std::collections;
use std::fmt;
pub(crate) struct BuiltinVariable {
pub(crate) eval: VariableEval,
pub(crate) name: VariableName,
}
impl BuiltinVariable {
fn new<F, S>(name: S, eval: F) -> Self
where
F: Fn(a1_notation::Address) -> EvalResult<Node> + 'static,
S: Into<String>,
{
Self {
name: name.into(),
eval: Box::new(eval),
}
}
pub fn all() -> collections::HashMap<String, BuiltinVariable> {
let mut vars = collections::HashMap::new();
vars = def_var(vars, "colleft", |a1| {
let col: RangeOrCell = a1.shift_left(1).column.into();
Ok(col.into())
});
vars = def_var(vars, "colright", |a1| {
let col: RangeOrCell = a1.shift_right(1).column.into();
Ok(col.into())
});
vars = def_var(vars, "colnum", |a1| Ok(((a1.column.x as i64) + 1).into()));
vars = def_var(vars, "cellref", |a1| Ok(a1.into()));
vars = def_var(vars, "colref", |a1| {
let col: RangeOrCell = a1.column.into();
Ok(col.into())
});
vars = def_var(vars, "rowabove", |a1| {
let row: RangeOrCell = a1.shift_up(1).row.into();
Ok(row.into())
});
vars = def_var(vars, "rowbelow", |a1| {
let row: RangeOrCell = a1.shift_down(1).row.into();
Ok(row.into())
});
vars = def_var(vars, "rownum", |a1| Ok(((a1.row.y as i64) + 1).into()));
vars = def_var(vars, "rowref", |a1| {
let row: RangeOrCell = a1.row.into();
Ok(row.into())
});
vars
}
}
fn def_var<F, S>(
mut vars: collections::HashMap<String, BuiltinVariable>,
name: S,
eval: F,
) -> collections::HashMap<String, BuiltinVariable>
where
F: Fn(Address) -> EvalResult<Node> + 'static,
S: Clone + Into<String>,
{
vars.insert(name.clone().into(), BuiltinVariable::new(name, eval));
vars
}
impl fmt::Debug for BuiltinVariable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BuiltinVariable")
.field("name", &self.name)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn colleft() {
let vars = BuiltinVariable::all();
let current = Address::new(4, 0);
let colleft = vars.get("colleft").unwrap();
assert_eq!((colleft.eval)(current).unwrap(), Node::reference("D:D"));
}
}