Skip to main content

dhttp_access/expr/
exprs.rs

1use std::{fmt::Display, ops::Index, slice::SliceIndex, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4
5use crate::expr::{
6    atomics::AtomicLocationRuleExpr,
7    eval::{BeOperator, BooleanOperator, EvalPolishError, Evaluable, VM},
8    parse::{self, TokenStream},
9};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum Part<Operator, Expr> {
13    Operator(Operator),
14    Expr(Expr),
15}
16
17impl<Operator: Display, Expr: Display> Display for Part<Operator, Expr> {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        match self {
20            Part::Operator(operator) => operator.fmt(f),
21            Part::Expr(expr) => expr.fmt(f),
22        }
23    }
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct Exprs<Operator, Expr> {
28    parts: Vec<Part<Operator, Expr>>,
29}
30
31impl<Operator, Expr, I> Index<I> for Exprs<Operator, Expr>
32where
33    I: SliceIndex<[Part<Operator, Expr>]>,
34{
35    type Output = <[Part<Operator, Expr>] as Index<I>>::Output;
36
37    fn index(&self, index: I) -> &Self::Output {
38        self.parts.index(index)
39    }
40}
41
42impl<Operator, Expr> IntoIterator for Exprs<Operator, Expr> {
43    type Item = Part<Operator, Expr>;
44
45    type IntoIter = std::vec::IntoIter<Part<Operator, Expr>>;
46
47    fn into_iter(self) -> Self::IntoIter {
48        self.parts.into_iter()
49    }
50}
51
52impl<'e, Operator, Expr> IntoIterator for &'e Exprs<Operator, Expr> {
53    type Item = &'e Part<Operator, Expr>;
54
55    type IntoIter = std::slice::Iter<'e, Part<Operator, Expr>>;
56
57    fn into_iter(self) -> Self::IntoIter {
58        self.parts.iter()
59    }
60}
61
62impl<Operator, Expr> From<Vec<Part<Operator, Expr>>> for Exprs<Operator, Expr> {
63    fn from(parts: Vec<Part<Operator, Expr>>) -> Self {
64        Self { parts }
65    }
66}
67
68impl<Operator, Expr> From<Part<Operator, Expr>> for Exprs<Operator, Expr> {
69    fn from(value: Part<Operator, Expr>) -> Self {
70        vec![value].into()
71    }
72}
73
74impl<Operator, Expr> Default for Exprs<Operator, Expr> {
75    fn default() -> Self {
76        Self {
77            parts: Default::default(),
78        }
79    }
80}
81
82impl<Operator, Expr> FromIterator<Part<Operator, Expr>> for Exprs<Operator, Expr> {
83    fn from_iter<T: IntoIterator<Item = Part<Operator, Expr>>>(iter: T) -> Self {
84        Self {
85            parts: iter.into_iter().collect(),
86        }
87    }
88}
89
90impl<Value, Operator, Expr, State> Evaluable<State> for Exprs<Operator, Expr>
91where
92    Operator: BeOperator<Value> + Clone,
93    Expr: Evaluable<State, Value = Value>,
94    State: ?Sized,
95{
96    type Value = Result<Value, EvalPolishError>;
97
98    fn eval(&self, state: &State) -> Self::Value {
99        self.try_eval(state)
100    }
101}
102
103impl<Operator, Expr> Exprs<Operator, Expr> {
104    pub fn try_eval<Value, State>(&self, state: &State) -> Result<Value, EvalPolishError>
105    where
106        Operator: BeOperator<Value> + Clone,
107        Expr: Evaluable<State, Value = Value>,
108        State: ?Sized,
109    {
110        VM::new().try_run(self.parts.iter().map(|part| match part {
111            Part::Operator(op) => Part::Operator(op.clone()),
112            Part::Expr(a) => Part::Expr(a.eval(state)),
113        }))
114    }
115
116    pub fn validate_arity(&self) -> Result<(), EvalPolishError>
117    where
118        Operator: BeOperator<()> + Clone,
119    {
120        VM::new()
121            .try_run(self.parts.iter().map(|part| match part {
122                Part::Operator(op) => Part::Operator(op.clone()),
123                Part::Expr(_) => Part::Expr(()),
124            }))
125            .map(|_| ())
126    }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
130pub struct LocationRuleExprs {
131    infix: String,
132    polish: Exprs<BooleanOperator, AtomicLocationRuleExpr>,
133}
134
135impl LocationRuleExprs {
136    pub fn infix(&self) -> &str {
137        &self.infix
138    }
139
140    pub fn polish(&self) -> &Exprs<BooleanOperator, AtomicLocationRuleExpr> {
141        &self.polish
142    }
143}
144
145impl Display for LocationRuleExprs {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        let Self { infix, .. } = self;
148        write!(f, "{infix}")
149    }
150}
151
152crate::orm_new_type!(@json LocationRuleExprs);
153
154impl<Operator: Display, Expr: Display> Serialize for Exprs<Operator, Expr> {
155    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
156    where
157        S: serde::Serializer,
158    {
159        let mut s = String::new();
160        self.parts
161            .iter()
162            .try_for_each(|part| {
163                use std::fmt::Write;
164                write!(s, "{part} ")
165            })
166            .map_err(serde::ser::Error::custom)?;
167        serializer.serialize_str(&s)
168    }
169}
170
171mod deserialize_exprs {
172    use peg::{error::ParseError, str::LineCol};
173    use serde::Deserializer;
174    use snafu::ResultExt;
175
176    use super::*;
177    use crate::expr::parse::InvalidPatternExpr;
178
179    #[derive(snafu::Snafu, Debug)]
180    pub enum DeserializeExprsError {
181        // issue: https://github.com/shepmaster/snafu/issues/99
182        String {
183            message: String,
184        },
185        #[snafu(display(
186            "failed to deserialize rule exprs while lexing `{input}` (internal error, database changed?)"
187        ))]
188        Lex {
189            input: String,
190            source: ParseError<LineCol>,
191        },
192        #[snafu(display(
193            "failed to deserialize rule exprs while parsing `{input}` (internal error, database changed?)"
194        ))]
195        Parse {
196            input: String,
197            source: ParseError<LineCol>,
198        },
199        #[snafu(display(
200            "failed to deserialize rule exprs while parsing pattern (internal error, database changed?)"
201        ))]
202        PatternParse {
203            source: InvalidPatternExpr,
204        },
205        #[snafu(display(
206            "failed to deserialize rule exprs while validating polish notation (internal error, database changed?)"
207        ))]
208        InvalidPolish {
209            source: EvalPolishError,
210        },
211    }
212
213    impl<'de> Deserialize<'de> for Exprs<BooleanOperator, AtomicLocationRuleExpr> {
214        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
215        where
216            D: Deserializer<'de>,
217        {
218            (|| -> Result<_, DeserializeExprsError> {
219                let polish = String::deserialize(deserializer).map_err(|e| {
220                    let message =
221                        format!("Deserialize rule exprs failed in deserializing string: {e}");
222                    StringSnafu { message }.build()
223                })?;
224                let tokens = TokenStream::new(&polish).context(LexSnafu { input: &polish })?;
225                let exprs = parse::polish_location_rule_exprs(&tokens)
226                    .context(ParseSnafu { input: &polish })?
227                    .context(PatternParseSnafu)?;
228                exprs.validate_arity().context(InvalidPolishSnafu)?;
229                Ok(exprs)
230            })()
231            .map_err(serde::de::Error::custom)
232        }
233    }
234}
235
236pub use deserialize_exprs::*;
237
238mod parse_exprs {
239    use peg::{error::ParseError, str::LineCol};
240    use snafu::ResultExt;
241
242    use super::*;
243    use crate::expr::parse::{self, InvalidPatternExpr};
244
245    #[derive(snafu::Snafu, Debug)]
246    pub enum ParseExprsError {
247        #[snafu(display("failed to parse rule exprs"))]
248        Pattern { source: InvalidPatternExpr },
249        #[snafu(display("failed to parse rule exprs `{input}`"))]
250        Incomplete {
251            input: String,
252            source: ParseError<LineCol>,
253        },
254    }
255
256    impl FromStr for LocationRuleExprs {
257        type Err = ParseExprsError;
258
259        fn from_str(infix: &str) -> Result<Self, Self::Err> {
260            let infix = infix.to_string();
261            let tokens =
262                parse::TokenStream::new(&infix).context(IncompleteSnafu { input: &infix })?;
263            let polish = parse::infix_location_rule_exprs(&tokens)
264                .context(IncompleteSnafu { input: &infix })?
265                .context(PatternSnafu)?;
266            Ok(Self { infix, polish })
267        }
268    }
269}
270
271#[cfg(feature = "cli")]
272impl clap::Args for LocationRuleExprs {
273    fn augment_args(cmd: clap::Command) -> clap::Command {
274        cmd.arg(
275            clap::Arg::new("expr")
276                .help("The expression to filter requests")
277                .required(true)
278                .num_args(1..)
279                .action(clap::ArgAction::Append),
280        )
281    }
282
283    fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
284        Self::augment_args(cmd)
285    }
286}
287
288#[cfg(feature = "cli")]
289impl clap::FromArgMatches for LocationRuleExprs {
290    fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
291        use clap::{Error, error::ErrorKind};
292
293        let patrs = matches
294            .get_many::<String>("expr")
295            .unwrap()
296            .cloned()
297            .collect::<Vec<_>>();
298
299        patrs
300            .join(" ")
301            .parse()
302            .map_err(snafu::Report::from_error)
303            .map_err(|e| Error::raw(ErrorKind::InvalidValue, e))
304    }
305
306    fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> {
307        *self = Self::from_arg_matches(matches)?;
308        Ok(())
309    }
310}