#![deny(missing_docs)]

//! This crate provides a library for working with logical expressions.
//!
//! It defines the `LogicalExpression` enum, which represents a logical expression with conditions
//! combined using AND and OR operators. The library also provides functions for parsing logical
//! expressions from strings and expanding them into a list of lists, where the inner lists represent
//! the AND conditions and the outer lists represent the OR conditions.

/// Represents a logical expression with conditions combined using AND and OR operators.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum LogicalExpression<Condition> {
    /// Represents a logical AND operation on a list of logical expressions.
    And(Vec<Self>),
    /// Represents a logical OR operation on a list of logical expressions.
    Or(Vec<Self>),
    /// Represents a single condition in the logical expression.
    Condition(Condition),
}

impl<Condition> LogicalExpression<Condition> {
    /// Creates a new logical expression representing the logical AND of the given list of expressions.
    ///
    /// If the list contains only one expression, it is returned as is.
    #[must_use]
    pub fn and(mut list: Vec<Self>) -> Self {
        if list.len() == 1 {
            return unsafe { list.pop().unwrap_unchecked() };
        }

        Self::And(list)
    }

    /// Creates a new logical expression representing the logical OR of the given list of expressions.
    ///
    /// If the list contains only one expression, it is returned as is.
    #[must_use]
    pub fn or(mut list: Vec<Self>) -> Self {
        if list.len() == 1 {
            return unsafe { list.pop().unwrap_unchecked() };
        }

        Self::Or(list)
    }
}

impl<Condition: Clone> LogicalExpression<Condition> {
    /// Expands the logical expression into a list of lists, where the inner lists represent the AND conditions
    /// and the outer lists represent the OR conditions.
    pub fn expand(self) -> Vec<Vec<Condition>> {
        match self {
            Self::And(groups) => {
                let expanded_groups: Vec<_> = groups.into_iter().map(Self::expand).collect();
                Self::cartesian_product(expanded_groups)
            }
            Self::Or(groups) => groups.into_iter().flat_map(Self::expand).collect(),
            Self::Condition(condition) => vec![vec![condition]],
        }
    }

    fn cartesian_product(groups: Vec<Vec<Vec<Condition>>>) -> Vec<Vec<Condition>> {
        let mut result = vec![Vec::new()];

        for group in groups {
            let mut new_result = vec![];
            for r in &result {
                for g in &group {
                    let mut new_r = r.clone();
                    new_r.extend(g.clone());
                    new_result.push(new_r);
                }
            }
            result = new_result;
        }

        result
    }
}

mod parser;

pub use parser::ParseError;
