use super::{CallError, FormalArgs, Item};
use crate::css::{CallArgs, Value};
use crate::input::SourcePos;
use crate::{Error, ScopeRef};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub struct Callable {
pub(crate) args: FormalArgs,
pub(crate) body: Vec<Item>,
pub(crate) decl: SourcePos,
}
impl Callable {
pub fn new(args: FormalArgs, body: Vec<Item>, decl: SourcePos) -> Self {
Self { args, body, decl }
}
pub fn no_args(body: Vec<Item>, decl: SourcePos) -> Self {
Self {
args: FormalArgs::none(),
body,
decl,
}
}
pub fn closure(&self, scope: &ScopeRef) -> Closure {
Closure {
body: self.clone(),
scope: scope.clone(),
}
}
}
#[derive(Clone)]
pub struct Closure {
pub(crate) body: Callable,
pub(crate) scope: ScopeRef,
}
impl Closure {
pub(crate) fn eval_args(
&self,
scope: ScopeRef,
args: CallArgs,
) -> Result<ScopeRef, CallError> {
self.body
.args
.eval(scope, args)
.map_err(|e| e.declared_at(&self.body.decl))
}
pub fn eval_value(&self, call: Call) -> Result<Value, CallError> {
Ok(self
.eval_args(
ScopeRef::sub_selectors(
self.scope.clone(),
call.scope.get_selectors().clone(),
),
call.args,
)?
.eval_body(&self.body.body)
.map_err(|e| match e {
Error::Invalid(err, _pos) => CallError::Invalid(err),
e => CallError::from(e),
})?
.unwrap_or(Value::Null))
}
}
impl std::cmp::PartialEq for Closure {
fn eq(&self, other: &Self) -> bool {
ScopeRef::is_same(&self.scope, &other.scope)
&& self.body == other.body
}
}
impl std::cmp::Eq for Closure {}
impl std::cmp::PartialOrd for Closure {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self.body.partial_cmp(&other.body) {
None => None,
Some(std::cmp::Ordering::Equal) => {
if ScopeRef::is_same(&self.scope, &other.scope) {
Some(std::cmp::Ordering::Equal)
} else {
None
}
}
Some(defined) => Some(defined),
}
}
}
pub struct Call {
pub(crate) args: CallArgs,
pub(crate) scope: ScopeRef,
}