1use std::fmt;
2
3#[cfg(any(test, feature = "arbitrary"))]
4use proptest::{arbitrary::ParamsFor, prelude::*};
5
6use crate::{
7 containers::ListOf,
8 error::{ParseError, ParseResult},
9 parser::{
10 debug_construction, impl_case_insensitive_str_primitive, impl_from_str, next_into_or,
11 rule_mismatch, ParserRule, TokenPair,
12 },
13};
14
15#[cfg(any(test, feature = "arbitrary"))]
16use crate::primitive::arbitrary::impl_rpsl_name_arbitrary;
17
18#[allow(clippy::module_name_repetitions)]
22pub type ActionExpr = Expr;
23impl_from_str!(ParserRule::just_action_expr => Expr);
24
25#[derive(Clone, Debug, Hash, PartialEq, Eq)]
26pub struct Expr(Vec<Stmt>);
27
28impl TryFrom<TokenPair<'_>> for Expr {
29 type Error = ParseError;
30
31 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
32 debug_construction!(pair => Expr);
33 match pair.as_rule() {
34 ParserRule::action_expr => Ok(Self(
35 pair.into_inner()
36 .map(Stmt::try_from)
37 .collect::<ParseResult<_>>()?,
38 )),
39 _ => Err(rule_mismatch!(pair => "action expression")),
40 }
41 }
42}
43
44impl fmt::Display for Expr {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 let mut s = self
47 .0
48 .iter()
49 .map(Stmt::to_string)
50 .collect::<Vec<_>>()
51 .join("; ");
52 s.push(';');
53 s.fmt(f)
54 }
55}
56
57#[cfg(any(test, feature = "arbitrary"))]
58impl Arbitrary for Expr {
59 type Parameters = ParamsFor<Stmt>;
60 type Strategy = BoxedStrategy<Self>;
61 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
62 prop::collection::vec(any_with::<Stmt>(params), 1..8)
63 .prop_map(Self)
64 .boxed()
65 }
66}
67
68#[derive(Clone, Debug, Hash, PartialEq, Eq)]
69pub enum Stmt {
70 Operator(OperatorStmt),
71 Method(MethodStmt),
72}
73
74impl TryFrom<TokenPair<'_>> for Stmt {
75 type Error = ParseError;
76
77 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
78 debug_construction!(pair => Stmt);
79 match pair.as_rule() {
80 ParserRule::action_stmt_oper => Ok(Self::Operator(pair.try_into()?)),
81 ParserRule::action_stmt_meth => Ok(Self::Method(pair.try_into()?)),
82 _ => Err(rule_mismatch!(pair => "action expression statement")),
83 }
84 }
85}
86
87impl fmt::Display for Stmt {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 match self {
90 Self::Operator(stmt) => stmt.fmt(f),
91 Self::Method(stmt) => stmt.fmt(f),
92 }
93 }
94}
95
96#[cfg(any(test, feature = "arbitrary"))]
97impl Arbitrary for Stmt {
98 type Parameters = ParamsFor<MethodStmt>;
99 type Strategy = BoxedStrategy<Self>;
100 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
101 prop_oneof![
102 any::<OperatorStmt>().prop_map(Self::Operator),
103 any_with::<MethodStmt>(params).prop_map(Self::Method),
104 ]
105 .boxed()
106 }
107}
108
109#[derive(Clone, Debug, Hash, PartialEq, Eq)]
110pub struct OperatorStmt {
111 prop: Property,
112 op: Operator,
113 val: Value,
114}
115
116impl TryFrom<TokenPair<'_>> for OperatorStmt {
117 type Error = ParseError;
118
119 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
120 debug_construction!(pair => OperatorStmt);
121 match pair.as_rule() {
122 ParserRule::action_stmt_oper => {
123 let mut pairs = pair.into_inner();
124 Ok(Self {
125 prop: next_into_or!(pairs => "failed to get action property")?,
126 op: next_into_or!(pairs => "failed to get action operator")?,
127 val: next_into_or!(pairs => "failed to get action operand")?,
128 })
129 }
130 _ => Err(rule_mismatch!(pair => "action expression operator statement")),
131 }
132 }
133}
134
135impl fmt::Display for OperatorStmt {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 write!(f, "{} {} {}", self.prop, self.op, self.val)
138 }
139}
140
141#[cfg(any(test, feature = "arbitrary"))]
142impl Arbitrary for OperatorStmt {
143 type Parameters = ();
144 type Strategy = BoxedStrategy<Self>;
145 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
146 (any::<Property>(), any::<Operator>(), any::<Value>())
147 .prop_map(|(prop, op, val)| Self { prop, op, val })
148 .boxed()
149 }
150}
151
152#[derive(Clone, Debug, Hash, PartialEq, Eq)]
153pub struct MethodStmt {
154 prop: Property,
155 method: Option<Method>,
156 vals: ListOf<Value>,
157}
158
159impl TryFrom<TokenPair<'_>> for MethodStmt {
160 type Error = ParseError;
161
162 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
163 debug_construction!(pair => MethodStmt);
164 match pair.as_rule() {
165 ParserRule::action_stmt_meth => {
166 let mut pairs = pair.into_inner().peekable();
167 let prop = next_into_or!(pairs => "failed to get action property")?;
168 let method =
169 if pairs.peek().map(TokenPair::as_rule) == Some(ParserRule::action_meth) {
170 Some(next_into_or!(pairs => "failed to get action method")?)
171 } else {
172 None
173 };
174 let vals = next_into_or!(pairs => "failed to get action operands")?;
175 Ok(Self { prop, method, vals })
176 }
177 _ => Err(rule_mismatch!(pair => "action expression method statement")),
178 }
179 }
180}
181
182impl fmt::Display for MethodStmt {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 write!(f, "{}", self.prop)?;
185 if let Some(method) = &self.method {
186 write!(f, ".{method}")?;
187 }
188 write!(f, "({})", self.vals)
189 }
190}
191
192#[cfg(any(test, feature = "arbitrary"))]
193impl Arbitrary for MethodStmt {
194 type Parameters = ParamsFor<Option<Method>>;
195 type Strategy = BoxedStrategy<Self>;
196 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
197 (
198 any::<Property>(),
199 any_with::<Option<Method>>(params),
200 any::<ListOf<Value>>(),
201 )
202 .prop_map(|(prop, method, vals)| Self { prop, method, vals })
203 .boxed()
204 }
205}
206
207#[derive(Clone, Debug, Hash, PartialEq, Eq)]
208pub(crate) enum Property {
209 Pref,
210 Med,
211 Dpa,
212 AsPath,
213 Community,
214 NextHop,
215 Cost,
216 Unknown(UnknownProperty),
217}
218
219impl TryFrom<TokenPair<'_>> for Property {
220 type Error = ParseError;
221
222 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
223 debug_construction!(pair => Property);
224 match pair.as_rule() {
225 ParserRule::rp_pref => Ok(Self::Pref),
226 ParserRule::rp_med => Ok(Self::Med),
227 ParserRule::rp_dpa => Ok(Self::Dpa),
228 ParserRule::rp_aspath => Ok(Self::AsPath),
229 ParserRule::rp_community => Ok(Self::Community),
230 ParserRule::rp_next_hop => Ok(Self::NextHop),
231 ParserRule::rp_cost => Ok(Self::Cost),
232 ParserRule::rp_unknown => Ok(Self::Unknown(pair.try_into()?)),
233 _ => Err(rule_mismatch!(pair => "action property name")),
234 }
235 }
236}
237
238impl fmt::Display for Property {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 match self {
241 Self::Pref => write!(f, "pref"),
242 Self::Med => write!(f, "med"),
243 Self::Dpa => write!(f, "dpa"),
244 Self::AsPath => write!(f, "aspath"),
245 Self::Community => write!(f, "community"),
246 Self::NextHop => write!(f, "next-hop"),
247 Self::Cost => write!(f, "cost"),
248 Self::Unknown(property) => property.fmt(f),
249 }
250 }
251}
252
253#[cfg(any(test, feature = "arbitrary"))]
254impl Arbitrary for Property {
255 type Parameters = ParamsFor<UnknownProperty>;
256 type Strategy = BoxedStrategy<Self>;
257 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
258 prop_oneof![
259 Just(Self::Pref),
260 Just(Self::Med),
261 Just(Self::Dpa),
262 Just(Self::AsPath),
263 Just(Self::Community),
264 Just(Self::NextHop),
265 Just(Self::Cost),
266 any_with::<UnknownProperty>(params).prop_map(Self::Unknown),
267 ]
268 .boxed()
269 }
270}
271
272#[derive(Clone, Debug)]
273pub(crate) struct UnknownProperty(String);
274impl_case_insensitive_str_primitive!(ParserRule::rp_unknown => UnknownProperty);
275#[cfg(any(test, feature = "arbitrary"))]
276impl_rpsl_name_arbitrary!(UnknownProperty);
277
278#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
279pub(crate) enum Operator {
280 Assign,
281 Append,
282 LshAssign,
283 RshAssign,
284 AddAssign,
285 SubAssign,
286 MulAssign,
287 DivAssign,
288 Eq,
289 Ne,
290 Le,
291 Ge,
292 Lt,
293 Gt,
294}
295
296impl TryFrom<TokenPair<'_>> for Operator {
297 type Error = ParseError;
298
299 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
300 debug_construction!(pair => Operator);
301 match pair.as_rule() {
302 ParserRule::action_op_assign => Ok(Self::Assign),
303 ParserRule::action_op_append => Ok(Self::Append),
304 ParserRule::action_op_lsh_assign => Ok(Self::LshAssign),
305 ParserRule::action_op_rsh_assign => Ok(Self::RshAssign),
306 ParserRule::action_op_add_assign => Ok(Self::AddAssign),
307 ParserRule::action_op_sub_assign => Ok(Self::SubAssign),
308 ParserRule::action_op_mul_assign => Ok(Self::MulAssign),
309 ParserRule::action_op_div_assign => Ok(Self::DivAssign),
310 ParserRule::action_op_eq => Ok(Self::Eq),
311 ParserRule::action_op_ne => Ok(Self::Ne),
312 ParserRule::action_op_le => Ok(Self::Le),
313 ParserRule::action_op_ge => Ok(Self::Ge),
314 ParserRule::action_op_lt => Ok(Self::Lt),
315 ParserRule::action_op_gt => Ok(Self::Gt),
316 _ => Err(rule_mismatch!(pair => "action expression operator")),
317 }
318 }
319}
320
321impl fmt::Display for Operator {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 match self {
324 Self::Assign => write!(f, "="),
325 Self::Append => write!(f, ".="),
326 Self::LshAssign => write!(f, "<<="),
327 Self::RshAssign => write!(f, ">>="),
328 Self::AddAssign => write!(f, "+="),
329 Self::SubAssign => write!(f, "-="),
330 Self::MulAssign => write!(f, "*="),
331 Self::DivAssign => write!(f, "/="),
332 Self::Eq => write!(f, "=="),
333 Self::Ne => write!(f, "!="),
334 Self::Le => write!(f, "<="),
335 Self::Ge => write!(f, ">="),
336 Self::Lt => write!(f, "<"),
337 Self::Gt => write!(f, ">"),
338 }
339 }
340}
341
342#[cfg(any(test, feature = "arbitrary"))]
343impl Arbitrary for Operator {
344 type Parameters = ();
345 type Strategy = BoxedStrategy<Self>;
346 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
347 prop_oneof![
348 Just(Self::Assign),
349 Just(Self::Append),
350 Just(Self::LshAssign),
351 Just(Self::RshAssign),
352 Just(Self::AddAssign),
353 Just(Self::SubAssign),
354 Just(Self::MulAssign),
355 Just(Self::DivAssign),
356 Just(Self::Eq),
357 Just(Self::Ne),
358 Just(Self::Le),
359 Just(Self::Ge),
360 Just(Self::Lt),
361 Just(Self::Gt),
362 ]
363 .boxed()
364 }
365}
366
367#[derive(Clone, Debug)]
368pub struct Method(String);
369impl_case_insensitive_str_primitive!(ParserRule::action_meth => Method);
370#[cfg(any(test, feature = "arbitrary"))]
371impl_rpsl_name_arbitrary!(Method);
372
373#[derive(Clone, Debug, Hash, PartialEq, Eq)]
374pub(crate) enum Value {
375 Unit(String),
376 List(Box<ListOf<Value>>),
377}
378
379impl TryFrom<TokenPair<'_>> for Value {
380 type Error = ParseError;
381
382 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
383 debug_construction!(pair => Value);
384 match pair.as_rule() {
385 ParserRule::action_val_nested => Ok(Self::List(Box::new(
386 next_into_or!(pair.into_inner() => "failed to get inner action operand list")?,
387 ))),
388 ParserRule::action_val_unit => Ok(Self::Unit(pair.as_str().to_string())),
389 _ => Err(rule_mismatch!(pair => "action operand")),
390 }
391 }
392}
393
394impl fmt::Display for Value {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 match self {
397 Self::List(val_list) => write!(f, "{{{val_list}}}"),
398 Self::Unit(val) => val.fmt(f),
399 }
400 }
401}
402
403#[cfg(any(test, feature = "arbitrary"))]
404impl Arbitrary for Value {
405 type Parameters = ();
406 type Strategy = BoxedStrategy<Self>;
407 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
408 let leaf = r"[0-9A-Za-z]+".prop_map(Self::Unit);
409 leaf.prop_recursive(1, 4, 4, |unit| {
410 proptest::collection::vec(unit, 0..4)
411 .prop_map(ListOf::from_iter)
412 .prop_map(Box::new)
413 .prop_map(Self::List)
414 })
415 .boxed()
416 }
417}
418
419#[cfg(test)]
497mod tests {
498 use super::*;
499 use crate::tests::{compare_ast, display_fmt_parses};
500
501 display_fmt_parses! {
502 ActionExpr,
503 }
504
505 compare_ast! {
506 ActionExpr {
507 rfc2622_sect6_autnum_example1: "pref = 1;" => {
508 Expr(vec![Stmt::Operator(OperatorStmt {
509 prop: Property::Pref,
510 op: Operator::Assign,
511 val: Value::Unit("1".into()),
512 })])
513 }
514 rfc2622_sect6_autnum_example2: "\
515 pref = 10; med = 0; community.append(10250, 3561:10);" => {
516 Expr(vec![
517 Stmt::Operator(OperatorStmt {
518 prop: Property::Pref,
519 op: Operator::Assign,
520 val: Value::Unit("10".into()),
521 }),
522 Stmt::Operator(OperatorStmt {
523 prop: Property::Med,
524 op: Operator::Assign,
525 val: Value::Unit("0".into()),
526 }),
527 Stmt::Method(MethodStmt {
528 prop: Property::Community,
529 method: Some("append".into()),
530 vals: vec![
531 Value::Unit("10250".into()),
532 Value::Unit("3561:10".into()),
533 ].into_iter().collect()
534 })
535 ])
536 }
537 regression1: "pref.RS-a(a);" => {
538 Expr(vec![Stmt::Method(MethodStmt {
539 prop: Property::Pref,
540 method: Some("RS-a".into()),
541 vals: vec![Value::Unit("a".into())].into_iter().collect(),
542 })])
543 }
544 regression2: "meD0 = a;" => {
545 Expr(vec![Stmt::Operator(OperatorStmt {
546 prop: Property::Unknown("meD0".into()),
547 op: Operator::Assign,
548 val: Value::Unit("a".into()),
549 })])
550 }
551 regression3: "meD0(a);" => {
552 Expr(vec![Stmt::Method(MethodStmt {
553 prop: Property::Unknown("meD0".into()),
554 method: None,
555 vals: vec![Value::Unit("a".into())].into_iter().collect(),
556 })])
557 }
558 regression4: "community .= { 70 };" => {
559 Expr(vec![Stmt::Operator(OperatorStmt {
560 prop: Property::Community,
561 op: Operator::Append,
562 val: Value::List(Box::new(vec![Value::Unit("70".into())].into_iter().collect())),
563 })])
564 }
565 regression5: "pref = { };" => {
566 Expr(vec![Stmt::Operator(OperatorStmt {
567 prop: Property::Pref,
568 op: Operator::Assign,
569 val: Value::List(Box::new(vec![].into_iter().collect())),
570 })])
571 }
572 }
573 }
574}