Skip to main content

genomicframe_core/formats/vcf/
expression.rs

1use crate::error::{Error, Result};
2use crate::expression::{extract_f64, extract_i64, extract_string, ExprToFilter};
3use crate::expression::{
4    extract_u32, CompiledAndFilter, CompiledNotFilter, CompiledOrFilter, Expr,
5};
6use crate::filters::RecordFilter;
7use crate::formats::vcf::filters::*;
8use crate::formats::vcf::VcfRecord;
9
10impl ExprToFilter<VcfRecord> for Expr {
11    fn compile(&self) -> Result<Box<dyn RecordFilter<VcfRecord>>> {
12        match self {
13            // Genomic predicates
14            Expr::IsTransition => Ok(Box::new(TransitionOnlyFilter)),
15            Expr::IsTransversion => Ok(Box::new(TransversionOnlyFilter)),
16            Expr::IsSnp => Ok(Box::new(SnpOnlyFilter)),
17            Expr::IsIndel => Ok(Box::new(IndelOnlyFilter)),
18            Expr::IsPass => Ok(Box::new(PassFilter)),
19
20            // Chromosome filter
21            Expr::OnChromosome(chrom) => Ok(Box::new(ChromosomeFilter {
22                chromosome: chrom.clone(),
23            })),
24
25            Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "chrom") => {
26                let chrom = extract_string(right)?;
27                Ok(Box::new(ChromosomeFilter::new(chrom)))
28            }
29
30            // Region filters
31            Expr::InRegion(interval) => Ok(Box::new(RegionFilter {
32                intervals: vec![interval.clone()],
33            })),
34
35            // Quality comparison
36            Expr::Gt(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
37                let min_qual = extract_f64(right)?;
38                Ok(Box::new(QualityFilter { min_qual }))
39            }
40
41            Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
42                let min_qual = extract_f64(right)?;
43                Ok(Box::new(QualityFilter { min_qual }))
44            }
45
46            // Quality less than
47            Expr::Lt(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
48                let max_qual = extract_f64(right)?;
49                Ok(Box::new(MaxQualityFilter { max_qual }))
50            }
51
52            Expr::Lte(left, right) if matches!(**left, Expr::Column(ref name) if name == "qual") => {
53                let max_qual = extract_f64(right)?;
54                Ok(Box::new(MaxQualityFilter { max_qual }))
55            }
56
57            // Depth comparison (INFO/DP)
58            Expr::Gt(left, right) if matches!(**left, Expr::Column(ref name) if name == "depth" || name == "dp") =>
59            {
60                let min_depth = extract_u32(right)?;
61                Ok(Box::new(DepthFilter { min_depth }))
62            }
63
64            Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "depth" || name == "dp") =>
65            {
66                let min_depth = extract_u32(right)?;
67                Ok(Box::new(DepthFilter { min_depth }))
68            }
69
70            // Bi-allelic filter
71            Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "num_alt") => {
72                if extract_i64(right)? == 1 {
73                    Ok(Box::new(BiAllelicFilter))
74                } else {
75                    Err(Error::invalid_input(
76                        "Only num_alt == 1 (bi-allelic) supported",
77                    ))
78                }
79            }
80
81            // Boolean logic - recursively compile and combine
82            Expr::And(exprs) => {
83                if exprs.is_empty() {
84                    return Err(Error::invalid_input("Empty AND expression"));
85                }
86
87                let mut result = exprs[0].compile()?;
88                for expr in &exprs[1..] {
89                    let next = expr.compile()?;
90                    result = Box::new(CompiledAndFilter {
91                        left: result,
92                        right: next,
93                    });
94                }
95                Ok(result)
96            }
97
98            Expr::Or(exprs) => {
99                if exprs.is_empty() {
100                    return Err(Error::invalid_input("Empty OR expression"));
101                }
102
103                let mut result = exprs[0].compile()?;
104                for expr in &exprs[1..] {
105                    let next = expr.compile()?;
106                    result = Box::new(CompiledOrFilter {
107                        left: result,
108                        right: next,
109                    });
110                }
111                Ok(result)
112            }
113
114            Expr::Not(expr) => {
115                let inner = expr.compile()?;
116                Ok(Box::new(CompiledNotFilter { inner }))
117            }
118
119            _ => Err(Error::invalid_input(format!(
120                "Expression not supported for VCF: {}",
121                self
122            ))),
123        }
124    }
125}