rust-expression 0.3.1

Calculator and solver for linear equations.
Documentation
pub type Number = f64;

#[derive(Debug, PartialEq, Clone)]
pub struct FunCall {
    pub name: String,
    pub params: Vec<Operand>,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Operand {
    Number(Number),
    Symbol(String),
    Term(Box<Term>),
    FunCall(FunCall),
}

impl Operand {
    pub fn is_symbol(&self, sym: &str) -> bool {
        matches!(self, Operand::Symbol(s) if s == sym)
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Operation {
    Add,
    Sub,
    Mul,
    Div,
    Rem,
    Pow,
}

#[derive(Debug, PartialEq, Clone)]
pub struct Term {
    pub op: Operation,
    pub lhs: Operand,
    pub rhs: Operand,
}

#[derive(Debug, PartialEq, Clone)]
pub struct CustomFunction {
    pub args: Vec<String>,
    pub body: Operand,
}

#[derive(Clone)]
pub struct BuildInFunction {
    pub name: String,
    pub arg: String,
    pub body: &'static dyn Fn(Number) -> Number,
}

impl PartialEq for BuildInFunction {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.arg == other.arg
    }
}

impl std::fmt::Debug for BuildInFunction {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("BuildInFunction")
            .field("name", &self.name)
            .field("arg", &self.arg)
            .finish()
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Function {
    Custom(CustomFunction),
    BuildIn(BuildInFunction),
}

impl Default for Function {
    fn default() -> Self {
        Function::Custom(CustomFunction {
            args: Vec::new(),
            body: Operand::Number(1.0),
        })
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
    Expression {
        op: Operand,
    },
    Assignment {
        sym: String,
        op: Operand,
    },
    SolveFor {
        lhs: Operand,
        rhs: Operand,
        sym: String,
    },
    Function {
        name: String,
        fun: Function,
    },
    Plot {
        name: String,
    },
}

#[cfg(test)]
mod tests {
    use super::*;
    fn create_term() -> Term {
        let lhs = Operand::Number(1.0);
        let rhs = Operand::Number(1.0);
        let op = Operation::Add;
        Term { op, lhs, rhs }
    }

    #[test]
    fn operand_is_symbol() {
        assert!(Operand::Symbol("x".to_string()).is_symbol("x"));
    }

    #[test]
    fn operand_is_not_symbol() {
        assert!(!Operand::Symbol("y".to_string()).is_symbol("x"));
        assert!(!Operand::Number(1.0).is_symbol("x"));
        assert!(!Operand::Term(Box::new(create_term())).is_symbol("x"));
    }
}