genomicframe-core 0.2.0

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

impl ExprToFilter<VcfRecord> for Expr {
    fn compile(&self) -> Result<Box<dyn RecordFilter<VcfRecord>>> {
        match self {
            // Genomic predicates
            Expr::IsTransition => Ok(Box::new(TransitionOnlyFilter)),
            Expr::IsTransversion => Ok(Box::new(TransversionOnlyFilter)),
            Expr::IsSnp => Ok(Box::new(SnpOnlyFilter)),
            Expr::IsIndel => Ok(Box::new(IndelOnlyFilter)),
            Expr::IsPass => Ok(Box::new(PassFilter)),

            // Chromosome filter
            Expr::OnChromosome(chrom) => Ok(Box::new(ChromosomeFilter {
                chromosome: chrom.clone(),
            })),

            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)))
            }

            // Region filters
            Expr::InRegion(interval) => Ok(Box::new(RegionFilter {
                intervals: vec![interval.clone()],
            })),

            // Quality comparison
            Expr::Gt(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
                let min_qual = extract_f64(right)?;
                Ok(Box::new(QualityFilter { min_qual }))
            }

            Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
                let min_qual = extract_f64(right)?;
                Ok(Box::new(QualityFilter { min_qual }))
            }

            // Quality less than
            Expr::Lt(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
                let max_qual = extract_f64(right)?;
                Ok(Box::new(MaxQualityFilter { max_qual }))
            }

            Expr::Lte(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
                let max_qual = extract_f64(right)?;
                Ok(Box::new(MaxQualityFilter { max_qual }))
            }

            // Depth comparison (INFO/DP)
            Expr::Gt(left, right) if matches!(**left, Expr::Column(ref name) if name == "depth" || name == "dp") =>
            {
                let min_depth = extract_u32(right)?;
                Ok(Box::new(DepthFilter { min_depth }))
            }

            Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "depth" || name == "dp") =>
            {
                let min_depth = extract_u32(right)?;
                Ok(Box::new(DepthFilter { min_depth }))
            }

            // Bi-allelic filter
            Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "num_alt") => {
                if extract_i64(right)? == 1 {
                    Ok(Box::new(BiAllelicFilter))
                } else {
                    Err(Error::invalid_input(
                        "Only num_alt == 1 (bi-allelic) supported",
                    ))
                }
            }

            // Boolean logic - recursively compile and combine
            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 VCF: {}",
                self
            ))),
        }
    }
}