Skip to main content

genomicframe_core/formats/bed/
expression.rs

1use crate::error::{Error, Result};
2use crate::expression::{
3    extract_string, CompiledAndFilter, CompiledNotFilter, CompiledOrFilter, Expr,
4};
5use crate::expression::{extract_u64, ExprToFilter};
6use crate::filters::RecordFilter;
7use crate::formats::bed::filters::*;
8use crate::formats::bed::BedRecord;
9
10impl ExprToFilter<BedRecord> for Expr {
11    fn compile(&self) -> Result<Box<dyn RecordFilter<BedRecord>>> {
12        match self {
13            // Chromosome filter (OnChromosome expression)
14            Expr::OnChromosome(chrom) => Ok(Box::new(ChromosomeFilter::new(chrom.clone()))),
15
16            // Chromosome filter (Eq expression)
17            Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "chrom") =>
18            {
19                let chrom = extract_string(right)?;
20                Ok(Box::new(ChromosomeFilter::new(chrom)))
21            }
22
23            // Strand filter
24            Expr::Eq(left, right) if matches!(**left, Expr::Column(ref name) if name == "strand") =>
25            {
26                let strand_str = extract_string(right)?;
27                let strand = if strand_str == "+" {
28                    crate::formats::bed::filters::Strand::Forward
29                } else if strand_str == "-" {
30                    crate::formats::bed::filters::Strand::Reverse
31                } else {
32                    return Err(Error::invalid_input("Strand must be '+' or '-'"));
33                };
34                Ok(Box::new(StrandFilter { strand }))
35            }
36
37            // Length filter
38            Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "length") =>
39            {
40                let min_length = extract_u64(right)?;
41                Ok(Box::new(MinLengthFilter { min_length }))
42            }
43            // TODO: Implement max length filter
44            // Expr::Lt(left, right) | Expr::Lte(left, right) if matches!(**left, Expr::Column(ref name) if name == "length") =>
45            // {
46            //     let max_length = extract_u64(right)?;
47            //     Ok(Box::new(MaxLengthFilter { max_length }))
48            // }
49
50            // Score filter
51            Expr::Gt(left, right) | Expr::Gte(left, right) if matches!(**left, Expr::Column(ref name) if name == "score") =>
52            {
53                let min_score = extract_u64(right)? as f64;
54                Ok(Box::new(MinScoreFilter { min_score }))
55            }
56
57            // Region filter
58            Expr::InRegion(interval) => Ok(Box::new(RegionFilter {
59                chrom: interval.chrom.clone(),
60                start: interval.start,
61                end: interval.end,
62            })),
63
64            // Boolean logic
65            Expr::And(exprs) => {
66                if exprs.is_empty() {
67                    return Err(Error::invalid_input("Empty AND expression"));
68                }
69
70                let mut result = exprs[0].compile()?;
71                for expr in &exprs[1..] {
72                    let next = expr.compile()?;
73                    result = Box::new(CompiledAndFilter {
74                        left: result,
75                        right: next,
76                    });
77                }
78                Ok(result)
79            }
80
81            Expr::Or(exprs) => {
82                if exprs.is_empty() {
83                    return Err(Error::invalid_input("Empty OR expression"));
84                }
85
86                let mut result = exprs[0].compile()?;
87                for expr in &exprs[1..] {
88                    let next = expr.compile()?;
89                    result = Box::new(CompiledOrFilter {
90                        left: result,
91                        right: next,
92                    });
93                }
94                Ok(result)
95            }
96
97            Expr::Not(expr) => {
98                let inner = expr.compile()?;
99                Ok(Box::new(CompiledNotFilter { inner }))
100            }
101
102            _ => Err(Error::invalid_input(format!(
103                "Expression not supported for BED: {}",
104                self
105            ))),
106        }
107    }
108}