genomicframe_core/formats/vcf/
expression.rs1use 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 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 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 Expr::InRegion(interval) => Ok(Box::new(RegionFilter {
32 intervals: vec![interval.clone()],
33 })),
34
35 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 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 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 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 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}