Skip to main content

snomed_ecl_engine/ecl/
filters.rs

1use super::{Comparison, Expr, ParseErrorKind, Parser, Result, MAX_DEPTH, MAX_NODES};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub enum ConceptFilter {
5    Active(Comparison, Option<bool>),
6    Defined(Comparison, Vec<bool>),
7    DefinitionStatus(Comparison, Box<Expr>),
8    Module(Comparison, Box<Expr>),
9    EffectiveTime(Comparison, Vec<Option<u32>>),
10}
11
12impl Parser<'_> {
13    pub(super) fn active_value(&mut self) -> Result<Option<bool>> {
14        if self.take("*") || self.keyword("any") || self.take("\"*\"") {
15            Ok(None)
16        } else if self.take("1") || self.keyword("true") {
17            Ok(Some(true))
18        } else if self.take("0") || self.keyword("false") {
19            Ok(Some(false))
20        } else {
21            Err(self.unexpected())
22        }
23    }
24    pub(super) fn concept_filters(&mut self, depth: usize) -> Result<Vec<ConceptFilter>> {
25        if depth > MAX_DEPTH {
26            return Err(self.error(ParseErrorKind::Limit, "Filter nesting exceeds 64"));
27        }
28        self.take("{{");
29        self.ws()?;
30        if !(self.take("C") || self.take("c")) {
31            return Err(self.unexpected());
32        }
33        self.ws()?;
34        let mut filters = Vec::new();
35        loop {
36            let name = self.filter_name();
37            self.pos += name.len();
38            let comparison = self.comparison()?;
39            if name != "effectivetime" && !matches!(comparison, Comparison::Eq | Comparison::Ne) {
40                return Err(self.error(
41                    ParseErrorKind::Syntax,
42                    "This filter requires equality or inequality",
43                ));
44            }
45            let filter = match name.as_str() {
46                "active" => ConceptFilter::Active(comparison, self.active_value()?),
47                "definitionstatus" => {
48                    let set = self.take("(");
49                    self.ws()?;
50                    let mut values = Vec::new();
51                    loop {
52                        values.push(if self.keyword("primitive") {
53                            false
54                        } else if self.keyword("defined") {
55                            true
56                        } else {
57                            return Err(self.unexpected());
58                        });
59                        let separated = self.ws()?;
60                        if !set || self.take(")") {
61                            break;
62                        }
63                        if !separated {
64                            return Err(self.unexpected());
65                        }
66                    }
67                    ConceptFilter::Defined(comparison, values)
68                }
69                "definitionstatusid" => ConceptFilter::DefinitionStatus(
70                    comparison,
71                    Box::new(self.filter_concepts(depth)?),
72                ),
73                "moduleid" => {
74                    ConceptFilter::Module(comparison, Box::new(self.filter_concepts(depth)?))
75                }
76                "effectivetime" => {
77                    let set = self.take("(");
78                    self.ws()?;
79                    let mut values = Vec::new();
80                    loop {
81                        values.push(self.filter_date()?);
82                        let separated = self.ws()?;
83                        if !set || self.take(")") {
84                            break;
85                        }
86                        if !separated {
87                            return Err(self.unexpected());
88                        }
89                    }
90                    ConceptFilter::EffectiveTime(comparison, values)
91                }
92                _ => return Err(self.error(ParseErrorKind::Syntax, "Unknown concept filter")),
93            };
94            self.nodes += 1;
95            if self.nodes > MAX_NODES {
96                return Err(self.error(ParseErrorKind::Limit, "Too many filter nodes"));
97            }
98            filters.push(filter);
99            self.ws()?;
100            if self.take("}}") {
101                return Ok(filters);
102            }
103            if !self.take(",") {
104                return Err(self.unexpected());
105            }
106            self.ws()?;
107        }
108    }
109
110    pub(super) fn filter_concepts(&mut self, depth: usize) -> Result<Expr> {
111        let saved = (self.pos, self.nodes);
112        if self.take("(") {
113            self.ws()?;
114            let mut values = Vec::new();
115            loop {
116                let start = self.pos;
117                while self.rest().starts_with(|c: char| c.is_ascii_digit()) {
118                    self.pos += 1;
119                }
120                let digits = &self.text[start..self.pos];
121                if !(6..=18).contains(&digits.len()) || digits.starts_with('0') {
122                    break;
123                }
124                let code = digits.parse().map_err(|_| self.unexpected())?;
125                values.push(self.node(Expr::Concept(code))?);
126                let mut separated = self.ws()?;
127                if self.take("|") {
128                    self.term()?;
129                    separated = self.ws()?;
130                }
131                if self.take(")") {
132                    if values.len() == 1 {
133                        // `(x)` is also a bracketed subexpression, which filters may
134                        // follow: `moduleId = (x) {{ C active = 1 }}`.
135                        break;
136                    }
137                    return self.node(Expr::Or(values));
138                }
139                if !separated {
140                    break;
141                }
142            }
143        }
144        (self.pos, self.nodes) = saved;
145        self.subexpression(depth)
146    }
147
148    pub(super) fn filter_date(&mut self) -> Result<Option<u32>> {
149        if !self.take("\"") {
150            return Err(self.unexpected());
151        }
152        if self.take("\"") {
153            return Ok(None);
154        }
155        let start = self.pos;
156        while self.rest().starts_with(|c: char| c.is_ascii_digit()) {
157            self.pos += 1;
158        }
159        let text = &self.text[start..self.pos];
160        if text.len() != 8 || text.starts_with('0') || !self.take("\"") {
161            return Err(self.error(
162                ParseErrorKind::Syntax,
163                "Expected YYYYMMDD or an empty effective time",
164            ));
165        }
166        let value: u32 = text.parse().map_err(|_| self.unexpected())?;
167        let at = start;
168        let year = value / 10000;
169        let month = value / 100 % 100;
170        let day = value % 100;
171        let days = match month {
172            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
173            4 | 6 | 9 | 11 => 30,
174            2 if year.is_multiple_of(400)
175                || year.is_multiple_of(4) && !year.is_multiple_of(100) =>
176            {
177                29
178            }
179            2 => 28,
180            _ => 0,
181        };
182        // The grammar bounds the month to 01-12 and the day to 01-31; a day the
183        // month lacks, such as 0931, is grammatical but names no date.
184        if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
185            return Err(self.error(ParseErrorKind::Syntax, "Invalid effective time date"));
186        }
187        if day > days {
188            self.refuse(at, "Effective time is not a calendar date");
189        }
190        Ok(Some(value))
191    }
192}