lla 0.5.4

Blazing Fast and highly customizable ls Replacement with Superpowers
use super::FileFilter;
use crate::error::{LlaError, Result};
use std::path::PathBuf;

#[derive(Debug, Clone, Copy)]
pub enum FilterOperation {
    And,
    Or,
    Not,
    Xor,
}

pub struct CompositeFilter {
    filters: Vec<Box<dyn FileFilter>>,
    operation: FilterOperation,
}

impl CompositeFilter {
    pub fn new(operation: FilterOperation) -> Self {
        CompositeFilter {
            filters: Vec::new(),
            operation,
        }
    }

    pub fn add_filter(&mut self, filter: Box<dyn FileFilter>) {
        self.filters.push(filter);
    }
}

impl FileFilter for CompositeFilter {
    fn filter_files(&self, files: &[PathBuf]) -> Result<Vec<PathBuf>> {
        if self.filters.is_empty() {
            return Ok(files.to_vec());
        }

        match self.operation {
            FilterOperation::And => {
                let mut result = files.to_vec();
                for filter in &self.filters {
                    result = filter
                        .filter_files(&result)
                        .map_err(|e| LlaError::Filter(format!("AND operation failed: {}", e)))?;
                }
                Ok(result)
            }
            FilterOperation::Or => {
                let mut result = Vec::new();
                for filter in &self.filters {
                    let filtered = filter
                        .filter_files(files)
                        .map_err(|e| LlaError::Filter(format!("OR operation failed: {}", e)))?;
                    for file in filtered {
                        if !result.contains(&file) {
                            result.push(file);
                        }
                    }
                }
                Ok(result)
            }
            FilterOperation::Not => {
                if self.filters.len() != 1 {
                    return Err(LlaError::Filter(
                        "NOT operation requires exactly one filter".to_string(),
                    ));
                }
                let filtered = self.filters[0]
                    .filter_files(files)
                    .map_err(|e| LlaError::Filter(format!("NOT operation failed: {}", e)))?;
                Ok(files
                    .iter()
                    .filter(|file| !filtered.contains(file))
                    .cloned()
                    .collect())
            }
            FilterOperation::Xor => {
                if self.filters.len() != 2 {
                    return Err(LlaError::Filter(
                        "XOR operation requires exactly two filters".to_string(),
                    ));
                }
                let first = self.filters[0].filter_files(files).map_err(|e| {
                    LlaError::Filter(format!("XOR operation failed on first filter: {}", e))
                })?;
                let second = self.filters[1].filter_files(files).map_err(|e| {
                    LlaError::Filter(format!("XOR operation failed on second filter: {}", e))
                })?;

                Ok(files
                    .iter()
                    .filter(|file| {
                        let in_first = first.contains(file);
                        let in_second = second.contains(file);
                        in_first ^ in_second
                    })
                    .cloned()
                    .collect())
            }
        }
    }
}