genomicframe_core/formats/bam/
expression.rs1use crate::error::{Error, Result};
2use crate::expression::{extract_string, extract_u8, ExprToFilter};
3use crate::expression::{
4 extract_u32, CompiledAndFilter, CompiledNotFilter, CompiledOrFilter, Expr,
5};
6use crate::filters::RecordFilter;
7use crate::formats::bam::filters::*;
8use crate::formats::bam::BamRecord;
9
10impl ExprToFilter<BamRecord> for Expr {
11 fn compile(&self) -> Result<Box<dyn RecordFilter<BamRecord>>> {
12 match self {
13 Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "chrom" || name == "rname" || name == "refname" || name == "refid") =>
16 {
17 let chrom = extract_string(right)?;
18 Ok(Box::new(ChromosomeFilter::new(chrom)))
19 }
20
21 Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "strand") =>
23 {
24 let strand_str = extract_string(right)?;
25 let strand = if strand_str == "+" {
26 crate::formats::bam::filters::Strand::Forward
27 } else if strand_str == "-" {
28 crate::formats::bam::filters::Strand::Reverse
29 } else {
30 return Err(Error::invalid_input("Strand must be '+' or '-'"));
31 };
32 Ok(Box::new(StrandFilter { strand }))
33 }
34
35 Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "mapq") =>
37 {
38 let min_mapq = extract_u8(right)?;
39 Ok(Box::new(MappingQualityFilter { min_mapq }))
40 }
41
42 Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "read_length") =>
44 {
45 let min_length = extract_u32(right)?;
46 Ok(Box::new(ReadLengthFilter {
47 min_length: min_length as usize,
48 max_length: None,
49 }))
50 }
51
52 Expr::Column(name) if name == "proper_pair" => Ok(Box::new(ProperPairFilter)),
54 Expr::Column(name) if name == "primary" => Ok(Box::new(PrimaryAlignmentFilter)),
55 Expr::Column(name) if name == "mapped" => Ok(Box::new(MappedOnlyFilter)),
56 Expr::Column(name) if name == "qc_pass" => Ok(Box::new(QcPassFilter)),
57 Expr::Column(name) if name == "first_in_pair" => Ok(Box::new(PairPositionFilter {
58 position: PairPosition::First,
59 })),
60 Expr::Column(name) if name == "second_in_pair" => Ok(Box::new(PairPositionFilter {
61 position: PairPosition::Second,
62 })),
63
64 Expr::Not(inner) if matches!(**inner, Expr::Column(ref name) if name == "duplicate") => {
66 Ok(Box::new(NoDuplicatesFilter))
67 }
68
69 Expr::And(exprs) => {
71 if exprs.is_empty() {
72 return Err(Error::invalid_input("Empty AND expression"));
73 }
74
75 let mut result = exprs[0].compile()?;
76 for expr in &exprs[1..] {
77 let next = expr.compile()?;
78 result = Box::new(CompiledAndFilter {
79 left: result,
80 right: next,
81 });
82 }
83 Ok(result)
84 }
85
86 Expr::Or(exprs) => {
87 if exprs.is_empty() {
88 return Err(Error::invalid_input("Empty OR expression"));
89 }
90
91 let mut result = exprs[0].compile()?;
92 for expr in &exprs[1..] {
93 let next = expr.compile()?;
94 result = Box::new(CompiledOrFilter {
95 left: result,
96 right: next,
97 });
98 }
99 Ok(result)
100 }
101
102 Expr::Not(expr) => {
103 let inner = expr.compile()?;
104 Ok(Box::new(CompiledNotFilter { inner }))
105 }
106
107 _ => Err(Error::invalid_input(format!(
108 "Expression not supported for BAM: {}",
109 self
110 ))),
111 }
112 }
113}