genomicframe-core 0.2.0

High-performance genomics I/O and interoperability layer
Documentation
use crate::error::{Error, Result};
use crate::expression::{
    extract_string, CompiledAndFilter, CompiledNotFilter, CompiledOrFilter, Expr,
};
use crate::expression::{extract_u64, ExprToFilter};
use crate::filters::RecordFilter;
use crate::formats::bed::filters::*;
use crate::formats::bed::BedRecord;

impl ExprToFilter<BedRecord> for Expr {
    fn compile(&self) -> Result<Box<dyn RecordFilter<BedRecord>>> {
        match self {
            // Chromosome filter (OnChromosome expression)
            Expr::OnChromosome(chrom) => Ok(Box::new(ChromosomeFilter::new(chrom.clone()))),

            // Chromosome filter (Eq expression)
            Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "chrom") =>
            {
                let chrom = extract_string(right)?;
                Ok(Box::new(ChromosomeFilter::new(chrom)))
            }

            // Strand filter
            Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "strand") =>
            {
                let strand_str = extract_string(right)?;
                let strand = if strand_str == "+" {
                    crate::formats::bed::filters::Strand::Forward
                } else if strand_str == "-" {
                    crate::formats::bed::filters::Strand::Reverse
                } else {
                    return Err(Error::invalid_input("Strand must be '+' or '-'"));
                };
                Ok(Box::new(StrandFilter { strand }))
            }

            // Length filter
            Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "length") =>
            {
                let min_length = extract_u64(right)?;
                Ok(Box::new(MinLengthFilter { min_length }))
            }
            // TODO: Implement max length filter
            // Expr::Lt(left, right) | Expr::Lte(left, right) if matches!(**left, Expr::Column(ref name) if name == "length") =>
            // {
            //     let max_length = extract_u64(right)?;
            //     Ok(Box::new(MaxLengthFilter { max_length }))
            // }

            // Score filter
            Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "score") =>
            {
                let min_score = extract_u64(right)? as f64;
                Ok(Box::new(MinScoreFilter { min_score }))
            }

            // Region filter
            Expr::InRegion(interval) => Ok(Box::new(RegionFilter {
                chrom: interval.chrom.clone(),
                start: interval.start,
                end: interval.end,
            })),

            // Boolean logic
            Expr::And(exprs) => {
                if exprs.is_empty() {
                    return Err(Error::invalid_input("Empty AND expression"));
                }

                let mut result = exprs[0].compile()?;
                for expr in &exprs[1..] {
                    let next = expr.compile()?;
                    result = Box::new(CompiledAndFilter {
                        left: result,
                        right: next,
                    });
                }
                Ok(result)
            }

            Expr::Or(exprs) => {
                if exprs.is_empty() {
                    return Err(Error::invalid_input("Empty OR expression"));
                }

                let mut result = exprs[0].compile()?;
                for expr in &exprs[1..] {
                    let next = expr.compile()?;
                    result = Box::new(CompiledOrFilter {
                        left: result,
                        right: next,
                    });
                }
                Ok(result)
            }

            Expr::Not(expr) => {
                let inner = expr.compile()?;
                Ok(Box::new(CompiledNotFilter { inner }))
            }

            _ => Err(Error::invalid_input(format!(
                "Expression not supported for BED: {}",
                self
            ))),
        }
    }
}