//! Logical operators: And, Or, Not

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters};
use crate::operator::Operator;
use crate::types::matcher::ExactMatcher;
use crate::types::{Type, BOOLEAN};

/// Logical NOT operator: `NOT x` or `!x`
#[derive(Default)]
pub struct Not;

impl FunctionDef for Not {
    fn name(&self) -> &'static str {
        Operator::Not.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new().with("x", ExactMatcher::of(BOOLEAN))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// Logical AND operator: `x AND y`
#[derive(Default)]
pub struct And;

impl FunctionDef for And {
    fn name(&self) -> &'static str {
        Operator::And.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(BOOLEAN))
            .with("right", ExactMatcher::of(BOOLEAN))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// Logical OR operator: `x OR y`
#[derive(Default)]
pub struct Or;

impl FunctionDef for Or {
    fn name(&self) -> &'static str {
        Operator::Or.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(BOOLEAN))
            .with("right", ExactMatcher::of(BOOLEAN))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}
