use crate::ErrorKind;
pub trait Context<N> {
fn variable(&self, name: &str) -> Option<N> {
let _ = name;
None
}
fn call(&self, name: &str, arguments: &[N]) -> Option<Result<N, ErrorKind>> {
let _ = (name, arguments);
None
}
}
impl<N> Context<N> for () {}
impl<N, C: Context<N> + ?Sized> Context<N> for &C {
fn variable(&self, name: &str) -> Option<N> {
(**self).variable(name)
}
fn call(&self, name: &str, arguments: &[N]) -> Option<Result<N, ErrorKind>> {
(**self).call(name, arguments)
}
}
impl<N, A: Context<N>, B: Context<N>> Context<N> for (A, B) {
fn variable(&self, name: &str) -> Option<N> {
self.0.variable(name).or_else(|| self.1.variable(name))
}
fn call(&self, name: &str, arguments: &[N]) -> Option<Result<N, ErrorKind>> {
self.0
.call(name, arguments)
.or_else(|| self.1.call(name, arguments))
}
}
impl<N: Copy> Context<N> for [(&str, N)] {
fn variable(&self, name: &str) -> Option<N> {
self.iter()
.find(|(candidate, _)| *candidate == name)
.map(|(_, value)| *value)
}
}
impl<N: Copy, const LENGTH: usize> Context<N> for [(&str, N); LENGTH] {
fn variable(&self, name: &str) -> Option<N> {
self.as_slice().variable(name)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Variables<F>(pub F);
impl<N, F: Fn(&str) -> Option<N>> Context<N> for Variables<F> {
fn variable(&self, name: &str) -> Option<N> {
(self.0)(name)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Functions<F>(pub F);
impl<N, F: Fn(&str, &[N]) -> Option<Result<N, ErrorKind>>> Context<N> for Functions<F> {
fn call(&self, name: &str, arguments: &[N]) -> Option<Result<N, ErrorKind>> {
(self.0)(name, arguments)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pairs_resolve_variables() {
let pairs = [("x", 1), ("y", 2)];
assert_eq!(pairs.variable("y"), Some(2));
assert_eq!(pairs.variable("z"), None);
assert_eq!(pairs.as_slice().variable("x"), Some(1));
}
#[test]
fn tuples_ask_the_first_context_first() {
let context = ([("x", 1)], [("x", 2), ("y", 3)]);
assert_eq!(context.variable("x"), Some(1));
assert_eq!(context.variable("y"), Some(3));
assert_eq!(context.call("f", &[]), None);
}
#[test]
fn unit_knows_nothing() {
assert_eq!(Context::<i32>::variable(&(), "x"), None);
assert_eq!(Context::<i32>::call(&(), "f", &[1]), None);
}
}