use crate::error::{Error, Result};
use crate::expression::{
extract_string, CompiledAndFilter, CompiledNotFilter, CompiledOrFilter, Expr,
};
use crate::expression::{extract_u64, ExprToFilter};
use crate::filters::RecordFilter;
use crate::formats::bed::filters::*;
use crate::formats::bed::BedRecord;
impl ExprToFilter<BedRecord> for Expr {
fn compile(&self) -> Result<Box<dyn RecordFilter<BedRecord>>> {
match self {
Expr::OnChromosome(chrom) => Ok(Box::new(ChromosomeFilter::new(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)))
}
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::bed::filters::Strand::Forward
} else if strand_str == "-" {
crate::formats::bed::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 == "length") =>
{
let min_length = extract_u64(right)?;
Ok(Box::new(MinLengthFilter { min_length }))
}
Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "score") =>
{
let min_score = extract_u64(right)? as f64;
Ok(Box::new(MinScoreFilter { min_score }))
}
Expr::InRegion(interval) => Ok(Box::new(RegionFilter {
chrom: interval.chrom.clone(),
start: interval.start,
end: interval.end,
})),
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 BED: {}",
self
))),
}
}
}