use crate::error::{Error, Result};
use crate::expression::{extract_string, extract_u8, ExprToFilter};
use crate::expression::{
extract_u32, CompiledAndFilter, CompiledNotFilter, CompiledOrFilter, Expr,
};
use crate::filters::RecordFilter;
use crate::formats::bam::filters::*;
use crate::formats::bam::BamRecord;
impl ExprToFilter<BamRecord> for Expr {
fn compile(&self) -> Result<Box<dyn RecordFilter<BamRecord>>> {
match self {
Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "chrom" || name == "rname" || name == "refname" || name == "refid") =>
{
let chrom = extract_string(right)?;
Ok(Box::new(ChromosomeFilter::new(chrom)))
}
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::bam::filters::Strand::Forward
} else if strand_str == "-" {
crate::formats::bam::filters::Strand::Reverse
} else {
return Err(Error::invalid_input("Strand must be '+' or '-'"));
};
Ok(Box::new(StrandFilter { strand }))
}
Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "mapq") =>
{
let min_mapq = extract_u8(right)?;
Ok(Box::new(MappingQualityFilter { min_mapq }))
}
Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "read_length") =>
{
let min_length = extract_u32(right)?;
Ok(Box::new(ReadLengthFilter {
min_length: min_length as usize,
max_length: None,
}))
}
Expr::Column(name) if name == "proper_pair" => Ok(Box::new(ProperPairFilter)),
Expr::Column(name) if name == "primary" => Ok(Box::new(PrimaryAlignmentFilter)),
Expr::Column(name) if name == "mapped" => Ok(Box::new(MappedOnlyFilter)),
Expr::Column(name) if name == "qc_pass" => Ok(Box::new(QcPassFilter)),
Expr::Column(name) if name == "first_in_pair" => Ok(Box::new(PairPositionFilter {
position: PairPosition::First,
})),
Expr::Column(name) if name == "second_in_pair" => Ok(Box::new(PairPositionFilter {
position: PairPosition::Second,
})),
Expr::Not(inner) if matches!(**inner, Expr::Column(ref name) if name == "duplicate") => {
Ok(Box::new(NoDuplicatesFilter))
}
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 BAM: {}",
self
))),
}
}
}