1use std::fmt;
2use std::str::FromStr;
3
4use ip::{traits::Prefix as _, AfiClass, Any, Ipv4, Ipv6};
5
6use time::{format_description::FormatItem, macros::format_description};
7
8#[cfg(any(test, feature = "arbitrary"))]
9use proptest::{arbitrary::ParamsFor, prelude::*};
10
11use crate::{
12 error::{err, ParseError, ParseResult},
13 names::AutNum,
14 parser::{
15 debug_construction, impl_case_insensitive_str_primitive, impl_from_str, impl_str_primitive,
16 next_into_or, next_parse_or, rule_mismatch, ParserRule, TokenPair,
17 },
18};
19
20#[cfg(any(test, feature = "arbitrary"))]
21use self::arbitrary::{impl_free_form_arbitrary, impl_rpsl_name_arbitrary, prop_filter_keywords};
22
23pub trait ParserAfi: AfiClass + 'static {
25 const LITERAL_ADDR_RULE: ParserRule;
27 const LITERAL_PREFIX_RULE: ParserRule;
29}
30impl ParserAfi for Ipv4 {
31 const LITERAL_ADDR_RULE: ParserRule = ParserRule::ipv4_addr;
32 const LITERAL_PREFIX_RULE: ParserRule = ParserRule::ipv4_prefix;
33}
34impl ParserAfi for Ipv6 {
35 const LITERAL_ADDR_RULE: ParserRule = ParserRule::ipv6_addr;
36 const LITERAL_PREFIX_RULE: ParserRule = ParserRule::ipv6_prefix;
37}
38impl ParserAfi for Any {
39 const LITERAL_ADDR_RULE: ParserRule = ParserRule::ip_addr_choice;
40 const LITERAL_PREFIX_RULE: ParserRule = ParserRule::ip_prefix_choice;
41}
42
43#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
45pub struct IpAddress<A: ParserAfi> {
46 inner: A::Address,
47}
48
49impl<A: ParserAfi> IpAddress<A> {
50 pub const fn new(address: A::Address) -> Self {
52 Self { inner: address }
53 }
54
55 pub const fn into_inner(self) -> A::Address {
57 self.inner
58 }
59}
60
61impl<A: ParserAfi> FromStr for IpAddress<A> {
62 type Err = ParseError;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 Ok(Self::new(s.parse()?))
66 }
67}
68
69impl<A: ParserAfi> TryFrom<TokenPair<'_>> for IpAddress<A> {
70 type Error = ParseError;
71
72 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
73 debug_construction!(pair => IpAddress);
74 match pair.as_rule() {
75 rule if rule == A::LITERAL_ADDR_RULE => Ok(pair.as_str().parse()?),
76 _ => Err(rule_mismatch!(pair => "IP address")),
77 }
78 }
79}
80
81impl<A: ParserAfi> fmt::Display for IpAddress<A> {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 self.inner.fmt(f)
84 }
85}
86
87#[cfg(any(test, feature = "arbitrary"))]
88impl<A> Arbitrary for IpAddress<A>
89where
90 A: ParserAfi + 'static,
91 A::Address: Arbitrary,
92 <A::Address as Arbitrary>::Strategy: 'static,
93{
94 type Parameters = ParamsFor<A::Address>;
95 type Strategy = BoxedStrategy<Self>;
96 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
97 any_with::<A::Address>(params).prop_map(Self::new).boxed()
98 }
99}
100
101#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
103pub struct IpPrefix<A: ParserAfi> {
104 inner: A::Prefix,
105}
106
107impl<A: ParserAfi> IpPrefix<A> {
108 pub const fn new(prefix: A::Prefix) -> Self {
110 Self { inner: prefix }
111 }
112
113 pub const fn into_inner(self) -> A::Prefix {
115 self.inner
116 }
117}
118
119impl<A: ParserAfi> FromStr for IpPrefix<A> {
120 type Err = ParseError;
121
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 Ok(Self::new(s.parse()?))
124 }
125}
126
127impl<A: ParserAfi> TryFrom<TokenPair<'_>> for IpPrefix<A> {
128 type Error = ParseError;
129
130 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
131 debug_construction!(pair => Prefix);
132 match pair.as_rule() {
133 rule if rule == A::LITERAL_PREFIX_RULE => Ok(pair.as_str().parse()?),
134 _ => Err(rule_mismatch!(pair => "IP prefix")),
135 }
136 }
137}
138
139impl<A: ParserAfi> fmt::Display for IpPrefix<A> {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 self.inner.fmt(f)
142 }
143}
144
145#[cfg(any(test, feature = "arbitrary"))]
146impl<A> Arbitrary for IpPrefix<A>
147where
148 A: ParserAfi + 'static,
149 A::Prefix: Arbitrary,
150{
151 type Parameters = ParamsFor<A::Prefix>;
152 type Strategy = BoxedStrategy<Self>;
153 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
154 any_with::<A::Prefix>(params).prop_map(Self::new).boxed()
155 }
156}
157
158#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
160pub struct IpPrefixRange<A: ParserAfi> {
161 prefix: IpPrefix<A>,
162 op: RangeOperator,
163}
164
165impl<A: ParserAfi> IpPrefixRange<A> {
166 pub const fn new(prefix: IpPrefix<A>, op: RangeOperator) -> Self {
168 Self { prefix, op }
169 }
170
171 pub const fn prefix(&self) -> IpPrefix<A> {
173 self.prefix
174 }
175
176 pub const fn operator(&self) -> RangeOperator {
178 self.op
179 }
180}
181
182impl<A: ParserAfi> TryFrom<TokenPair<'_>> for IpPrefixRange<A> {
183 type Error = ParseError;
184
185 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
186 debug_construction!(pair => PrefixRange);
187 let mut pairs = pair.into_inner();
188 let prefix = next_parse_or!(pairs => "failed to get inner prefix")?;
189 let op = match pairs.next() {
190 Some(inner) => inner.try_into()?,
191 None => RangeOperator::None,
192 };
193 Ok(Self { prefix, op })
194 }
195}
196
197impl<A: ParserAfi> fmt::Display for IpPrefixRange<A> {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "{}{}", self.prefix, self.op)
200 }
201}
202
203#[cfg(any(test, feature = "arbitrary"))]
204impl<A> Arbitrary for IpPrefixRange<A>
205where
206 A: ParserAfi + 'static,
207 A::Prefix: Arbitrary,
208 <A::Prefix as ip::traits::Prefix>::Length: AsRef<u8>,
209{
210 type Parameters = ParamsFor<IpPrefix<A>>;
211 type Strategy = BoxedStrategy<Self>;
212 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
213 any_with::<IpPrefix<A>>(params)
214 .prop_flat_map(|prefix| {
215 let len = *prefix.into_inner().prefix_len().as_ref();
216 let max_len = *prefix.into_inner().max_prefix_len().as_ref();
217 (Just(prefix), any_with::<RangeOperator>((len, max_len)))
218 })
219 .prop_map(|(prefix, op)| Self::new(prefix, op))
220 .boxed()
221 }
222}
223
224#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
228pub enum RangeOperator {
229 None,
231 LessExcl,
233 LessIncl,
235 Exact(u8),
237 Range(u8, u8),
239}
240
241impl TryFrom<TokenPair<'_>> for RangeOperator {
242 type Error = ParseError;
243
244 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
245 debug_construction!(pair => PrefixOp);
246 match pair.as_rule() {
247 ParserRule::less_excl => Ok(Self::LessExcl),
248 ParserRule::less_incl => Ok(Self::LessIncl),
249 ParserRule::exact => Ok(Self::Exact(
250 next_parse_or!(pair.into_inner() => "failed to get operand for range operation")?,
251 )),
252 ParserRule::range => {
253 let mut pairs = pair.into_inner();
254 Ok(Self::Range(
255 next_parse_or!(pairs => "failed to get lower operand for range operation")?,
256 next_parse_or!(pairs => "failed to get upper operand for range operation")?,
257 ))
258 }
259 _ => Err(err!(
260 "expected a prefix range operation, got {:?}: {}",
261 pair.as_rule(),
262 pair.as_str()
263 )),
264 }
265 }
266}
267
268impl fmt::Display for RangeOperator {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 match self {
271 Self::None => write!(f, ""),
272 Self::LessExcl => write!(f, "^-"),
273 Self::LessIncl => write!(f, "^+"),
274 Self::Exact(n) => write!(f, "^{n}"),
275 Self::Range(m, n) => write!(f, "^{m}-{n}"),
276 }
277 }
278}
279
280#[cfg(any(test, feature = "arbitrary"))]
281impl Arbitrary for RangeOperator {
282 type Parameters = (u8, u8);
283 type Strategy = BoxedStrategy<Self>;
284 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
285 prop_oneof![
286 Just(Self::None),
287 Just(Self::LessExcl),
288 Just(Self::LessIncl),
289 (args.0..=args.1).prop_map(Self::Exact),
290 (args.0..=args.1)
291 .prop_flat_map(move |lower| (Just(lower), lower..=args.1))
292 .prop_map(|(lower, upper)| Self::Range(lower, upper))
293 ]
294 .boxed()
295 }
296}
297
298#[allow(variant_size_differences)]
303#[derive(Clone, Debug, Hash, PartialEq, Eq)]
304pub enum SetNameComp {
305 AutNum(AutNum),
307 PeerAs(PeerAs),
309 Name(SetNameCompName),
311}
312
313impl TryFrom<TokenPair<'_>> for SetNameComp {
314 type Error = ParseError;
315
316 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
317 debug_construction!(pair => SetNameComp);
318 match pair.as_rule() {
319 ParserRule::aut_num => Ok(Self::AutNum(pair.as_str().parse()?)),
320 ParserRule::peeras => Ok(Self::PeerAs(PeerAs)),
321 ParserRule::filter_set_name
322 | ParserRule::route_set_name
323 | ParserRule::as_set_name
324 | ParserRule::rtr_set_name
325 | ParserRule::peering_set_name => Ok(Self::Name(pair.try_into()?)),
326 _ => Err(rule_mismatch!(pair => "set name component")),
327 }
328 }
329}
330
331impl fmt::Display for SetNameComp {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 match self {
334 Self::AutNum(autnum) => autnum.fmt(f),
335 Self::PeerAs(_) => write!(f, "PeerAS"),
336 Self::Name(name) => name.fmt(f),
337 }
338 }
339}
340
341#[cfg(any(test, feature = "arbitrary"))]
342impl Arbitrary for SetNameComp {
343 type Parameters = ParamsFor<SetNameCompName>;
344 type Strategy = BoxedStrategy<Self>;
345 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
346 prop_oneof![
347 any::<AutNum>().prop_map(Self::AutNum),
348 Just(Self::PeerAs(PeerAs)),
349 any_with::<SetNameCompName>(params).prop_map(Self::Name),
350 ]
351 .boxed()
352 }
353}
354
355#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
360pub struct PeerAs;
361
362#[derive(Clone, Debug)]
367pub struct SetNameCompName(String);
368impl_case_insensitive_str_primitive!(
369 ParserRule::filter_set_name
370 | ParserRule::route_set_name
371 | ParserRule::as_set_name
372 | ParserRule::rtr_set_name
373 | ParserRule::peering_set_name => SetNameCompName
374);
375
376#[cfg(any(test, feature = "arbitrary"))]
377impl Arbitrary for SetNameCompName {
378 type Parameters = ParamsFor<String>;
379 type Strategy = BoxedStrategy<Self>;
380 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
381 prop_filter_keywords(any_with::<String>(params))
382 .prop_map(Self)
383 .boxed()
384 }
385}
386
387#[derive(Clone, Debug)]
392pub struct ObjectDescr(String);
393impl_case_insensitive_str_primitive!(ParserRule::object_descr => ObjectDescr);
394#[cfg(any(test, feature = "arbitrary"))]
395impl_free_form_arbitrary!(ObjectDescr);
396
397#[derive(Clone, Debug)]
402pub struct NicHdl(String);
403impl_case_insensitive_str_primitive!(ParserRule::nic_hdl => NicHdl);
404#[cfg(any(test, feature = "arbitrary"))]
405impl_rpsl_name_arbitrary!(NicHdl);
406
407#[derive(Clone, Debug)]
412pub struct Remarks(String);
413impl_case_insensitive_str_primitive!(ParserRule::remarks => Remarks);
414#[cfg(any(test, feature = "arbitrary"))]
415impl_free_form_arbitrary!(Remarks);
416
417#[derive(Clone, Debug)]
422pub struct RegistryName(String);
423impl_case_insensitive_str_primitive!(ParserRule::registry_name => RegistryName);
424#[cfg(any(test, feature = "arbitrary"))]
425impl_rpsl_name_arbitrary!(RegistryName);
426
427#[derive(Clone, Debug)]
432pub struct Address(String);
433impl_case_insensitive_str_primitive!(ParserRule::address => Address);
434#[cfg(any(test, feature = "arbitrary"))]
435impl_free_form_arbitrary!(Address);
436
437#[derive(Clone, Debug)]
442pub struct EmailAddress(String);
443impl_case_insensitive_str_primitive!(ParserRule::email_addr => EmailAddress);
444
445#[cfg(any(test, feature = "arbitrary"))]
446impl Arbitrary for EmailAddress {
447 type Parameters = ();
448 type Strategy = BoxedStrategy<Self>;
449 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
450 (any::<DnsName>(), any::<DnsName>())
451 .prop_map(|(user, host)| format!("{user}@{host}"))
452 .prop_map(Self)
453 .boxed()
454 }
455}
456
457#[derive(Clone, Debug)]
462pub struct TelNumber(String);
463impl_case_insensitive_str_primitive!(ParserRule::tel_number => TelNumber);
464
465#[cfg(any(test, feature = "arbitrary"))]
466impl Arbitrary for TelNumber {
467 type Parameters = ();
468 type Strategy = BoxedStrategy<Self>;
469 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
470 r"\+[0-9][0-9 ]*( ext\. [0-9]+)?".prop_map(Self).boxed()
471 }
472}
473
474#[derive(Clone, Debug)]
480pub struct EmailAddressRegex(String);
481impl_case_insensitive_str_primitive!(ParserRule::email_addr_regexp => EmailAddressRegex);
482#[cfg(any(test, feature = "arbitrary"))]
483impl_free_form_arbitrary!(EmailAddressRegex);
484
485#[derive(Clone, Debug)]
490pub struct PgpFromFingerprint(String);
491impl_case_insensitive_str_primitive!(ParserRule::pgp_from_fingerpr => PgpFromFingerprint);
492#[cfg(any(test, feature = "arbitrary"))]
493impl_free_form_arbitrary!(PgpFromFingerprint);
494
495#[derive(Clone, Debug, Hash, PartialEq, Eq)]
500pub struct CryptHash(String);
501impl_str_primitive!(ParserRule::crypt_hash => CryptHash);
502#[cfg(any(test, feature = "arbitrary"))]
503impl_free_form_arbitrary!(CryptHash);
504
505#[derive(Clone, Debug)]
510pub struct Trouble(String);
511impl_case_insensitive_str_primitive!(ParserRule::trouble => Trouble);
512#[cfg(any(test, feature = "arbitrary"))]
513impl_free_form_arbitrary!(Trouble);
514
515#[derive(Clone, Debug)]
520pub struct KeyOwner(String);
521impl_case_insensitive_str_primitive!(ParserRule::owner => KeyOwner);
522#[cfg(any(test, feature = "arbitrary"))]
523impl_free_form_arbitrary!(KeyOwner);
524
525#[derive(Clone, Debug)]
530pub struct Fingerprint(String);
531impl_case_insensitive_str_primitive!(ParserRule::key_fingerprint => Fingerprint);
532
533#[cfg(any(test, feature = "arbitrary"))]
534impl Arbitrary for Fingerprint {
535 type Parameters = ();
536 type Strategy = BoxedStrategy<Self>;
537 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
538 r"[0-9A-Fa-f]+".prop_map(Self).boxed()
539 }
540}
541
542#[derive(Clone, Debug, Hash, PartialEq, Eq)]
547pub struct Certificate(String);
548impl_str_primitive!(ParserRule::key_certif => Certificate);
549#[cfg(any(test, feature = "arbitrary"))]
550impl_free_form_arbitrary!(Certificate);
551
552#[derive(Clone, Debug)]
557pub struct AsName(String);
558impl_case_insensitive_str_primitive!(ParserRule::as_name => AsName);
559#[cfg(any(test, feature = "arbitrary"))]
560impl_rpsl_name_arbitrary!(AsName);
561
562#[derive(Clone, Debug)]
567pub struct Netname(String);
568impl_case_insensitive_str_primitive!(ParserRule::netname => Netname);
569#[cfg(any(test, feature = "arbitrary"))]
570impl_rpsl_name_arbitrary!(Netname);
571
572#[derive(Clone, Debug)]
576pub struct CountryCode(String);
577impl_case_insensitive_str_primitive!(ParserRule::country_code => CountryCode);
578
579#[cfg(any(test, feature = "arbitrary"))]
580impl Arbitrary for CountryCode {
581 type Parameters = ();
582 type Strategy = BoxedStrategy<Self>;
583 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
584 r"[A-Za-z]{2}".prop_map(Self).boxed()
585 }
586}
587
588#[derive(Clone, Debug)]
593pub struct DnsName(String);
594impl_case_insensitive_str_primitive!(ParserRule::dns_name => DnsName);
595
596#[cfg(any(test, feature = "arbitrary"))]
597impl Arbitrary for DnsName {
598 type Parameters = ();
599 type Strategy = BoxedStrategy<Self>;
600 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
601 prop_filter_keywords(r"[A-Za-z][0-9A-Za-z_-]*(\.[A-Za-z][0-9A-Za-z_-]*)*")
602 .prop_map(Self)
603 .boxed()
604 }
605}
606
607#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
612pub struct Date(time::Date);
613
614const DATE_FMT: &[FormatItem<'_>] = format_description!("[year][month][day]");
615
616impl AsRef<time::Date> for Date {
617 fn as_ref(&self) -> &time::Date {
618 &self.0
619 }
620}
621
622impl FromStr for Date {
623 type Err = ParseError;
624 fn from_str(s: &str) -> ParseResult<Self> {
625 Ok(Self(time::Date::parse(s, DATE_FMT)?))
626 }
627}
628
629impl TryFrom<TokenPair<'_>> for Date {
630 type Error = ParseError;
631
632 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
633 debug_construction!(pair => Date);
634 match pair.as_rule() {
635 ParserRule::date => pair.as_str().parse(),
636 _ => Err(rule_mismatch!(pair => "date")),
637 }
638 }
639}
640
641impl fmt::Display for Date {
642 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
643 write!(f, "{}", self.0.format(DATE_FMT).map_err(|_| fmt::Error)?)
644 }
645}
646
647#[cfg(any(test, feature = "arbitrary"))]
648impl Arbitrary for Date {
649 type Parameters = ();
650 type Strategy = BoxedStrategy<Self>;
651 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
652 (time::macros::date!(0000 - 01 - 01).to_julian_day()..time::Date::MAX.to_julian_day())
653 .prop_map(|day| Self(time::Date::from_julian_day(day).unwrap()))
654 .boxed()
655 }
656}
657
658#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
663pub enum SigningMethod {
664 Pgp,
666 X509,
668}
669
670impl TryFrom<TokenPair<'_>> for SigningMethod {
671 type Error = ParseError;
672
673 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
674 debug_construction!(pair => SigningMethod);
675 match pair.as_rule() {
676 ParserRule::signing_method_pgp => Ok(Self::Pgp),
677 ParserRule::signing_method_x509 => Ok(Self::X509),
678 _ => Err(rule_mismatch!(pair => "signing method")),
679 }
680 }
681}
682
683impl fmt::Display for SigningMethod {
684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
685 match self {
686 Self::Pgp => write!(f, "PGP"),
687 Self::X509 => write!(f, "X509"),
688 }
689 }
690}
691
692#[cfg(any(test, feature = "arbitrary"))]
693impl Arbitrary for SigningMethod {
694 type Parameters = ();
695 type Strategy = BoxedStrategy<Self>;
696 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
697 prop_oneof![Just(Self::Pgp), Just(Self::X509)].boxed()
698 }
699}
700
701#[derive(Clone, Debug, Hash, PartialEq, Eq)]
706pub enum Protocol {
707 Bgp4,
709 MpBgp,
711 Ospf,
713 RipNg,
715 Rip,
717 Igrp,
719 IsIs,
721 Static,
723 Dvmrp,
725 PimDm,
727 PimSm,
729 Cbt,
731 Mospf,
733 Unknown(UnknownProtocol),
735}
736
737impl TryFrom<TokenPair<'_>> for Protocol {
738 type Error = ParseError;
739
740 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
741 debug_construction!(pair => Protocol);
742 match pair.as_rule() {
743 ParserRule::protocol_bgp4 => Ok(Self::Bgp4),
744 ParserRule::protocol_mpbgp => Ok(Self::MpBgp),
745 ParserRule::protocol_ospf => Ok(Self::Ospf),
746 ParserRule::protocol_ripng => Ok(Self::RipNg),
747 ParserRule::protocol_rip => Ok(Self::Rip),
748 ParserRule::protocol_igrp => Ok(Self::Igrp),
749 ParserRule::protocol_isis => Ok(Self::IsIs),
750 ParserRule::protocol_static => Ok(Self::Static),
751 ParserRule::protocol_dvmrp => Ok(Self::Dvmrp),
752 ParserRule::protocol_pim_dm => Ok(Self::PimDm),
753 ParserRule::protocol_pim_sm => Ok(Self::PimSm),
754 ParserRule::protocol_cbt => Ok(Self::Cbt),
755 ParserRule::protocol_mospf => Ok(Self::Mospf),
756 ParserRule::protocol_unknown => Ok(Self::Unknown(pair.try_into()?)),
757 _ => Err(rule_mismatch!(pair => "protocol name")),
758 }
759 }
760}
761
762impl fmt::Display for Protocol {
763 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
764 match self {
765 Self::Bgp4 => write!(f, "BGP4"),
766 Self::MpBgp => write!(f, "MPBGP"),
767 Self::Ospf => write!(f, "OSPF"),
768 Self::RipNg => write!(f, "RIPng"),
769 Self::Rip => write!(f, "RIP"),
770 Self::Igrp => write!(f, "IGRP"),
771 Self::IsIs => write!(f, "IS-IS"),
772 Self::Static => write!(f, "STATIC"),
773 Self::Dvmrp => write!(f, "DVMRP"),
774 Self::PimDm => write!(f, "PIM-DM"),
775 Self::PimSm => write!(f, "PIM-SM"),
776 Self::Cbt => write!(f, "CBT"),
777 Self::Mospf => write!(f, "MOSPF"),
778 Self::Unknown(name) => write!(f, "{name}"),
779 }
780 }
781}
782
783#[cfg(any(test, feature = "arbitrary"))]
784impl Arbitrary for Protocol {
785 type Parameters = ();
786 type Strategy = BoxedStrategy<Self>;
787 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
788 prop_oneof![
789 Just(Self::Bgp4),
790 Just(Self::MpBgp),
791 Just(Self::Ospf),
792 Just(Self::RipNg),
793 Just(Self::Rip),
794 Just(Self::Igrp),
795 Just(Self::IsIs),
796 Just(Self::Static),
797 Just(Self::Dvmrp),
798 Just(Self::PimDm),
799 Just(Self::PimSm),
800 Just(Self::Cbt),
801 Just(Self::Mospf),
802 any::<UnknownProtocol>().prop_map(Self::Unknown)
803 ]
804 .boxed()
805 }
806}
807
808#[derive(Clone, Debug)]
810pub struct UnknownProtocol(String);
811impl_case_insensitive_str_primitive!(ParserRule::protocol_unknown => UnknownProtocol);
812#[cfg(any(test, feature = "arbitrary"))]
813impl_rpsl_name_arbitrary!(UnknownProtocol);
814
815#[derive(Clone, Debug)]
820pub struct PeerOptKey(String);
821impl_case_insensitive_str_primitive!(ParserRule::peer_opt_key => PeerOptKey);
822#[cfg(any(test, feature = "arbitrary"))]
823impl_rpsl_name_arbitrary!(PeerOptKey);
824
825#[derive(Clone, Debug)]
830pub struct PeerOptVal(String);
831impl_case_insensitive_str_primitive!(ParserRule::peer_opt_val => PeerOptVal);
832
833#[cfg(any(test, feature = "arbitrary"))]
834impl Arbitrary for PeerOptVal {
835 type Parameters = ();
836 type Strategy = BoxedStrategy<Self>;
837 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
838 r"[^\);\pC]+".prop_map(Self).boxed()
839 }
840}
841
842#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
847pub struct AfiSafi {
848 afi: Afi,
849 safi: Option<Safi>,
850}
851
852impl_from_str!(ParserRule::afi_safi => AfiSafi);
853
854impl TryFrom<TokenPair<'_>> for AfiSafi {
855 type Error = ParseError;
856
857 #[allow(clippy::similar_names)]
858 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
859 debug_construction!(pair => AfiSafi);
860 match pair.as_rule() {
861 ParserRule::afi_safi => {
862 let mut pairs = pair.into_inner();
863 let afi = next_into_or!(pairs => "failed to get afi name")?;
864 let safi = if let Some(inner_pair) = pairs.next() {
865 Some(inner_pair.try_into()?)
866 } else {
867 None
868 };
869 Ok(Self { afi, safi })
870 }
871 _ => Err(rule_mismatch!(pair => "afi/safi identifier")),
872 }
873 }
874}
875
876impl fmt::Display for AfiSafi {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 write!(f, "{}", self.afi)?;
879 if let Some(safi) = &self.safi {
880 write!(f, ".{safi}")?;
881 }
882 Ok(())
883 }
884}
885
886#[cfg(any(test, feature = "arbitrary"))]
887impl Arbitrary for AfiSafi {
888 type Parameters = ParamsFor<Option<Safi>>;
889 type Strategy = BoxedStrategy<Self>;
890 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
891 (any::<Afi>(), any_with::<Option<Safi>>(params))
892 .prop_map(|(afi, safi)| Self { afi, safi })
893 .boxed()
894 }
895}
896
897#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
902pub enum Afi {
903 Ipv4,
905 Ipv6,
907 Any,
909}
910
911impl TryFrom<TokenPair<'_>> for Afi {
912 type Error = ParseError;
913
914 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
915 debug_construction!(pair => Afi);
916 match pair.as_rule() {
917 ParserRule::afi_ipv4 => Ok(Self::Ipv4),
918 ParserRule::afi_ipv6 => Ok(Self::Ipv6),
919 ParserRule::afi_any => Ok(Self::Any),
920 _ => Err(rule_mismatch!(pair => "afi identifier")),
921 }
922 }
923}
924
925impl fmt::Display for Afi {
926 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
927 match self {
928 Self::Ipv4 => write!(f, "ipv4"),
929 Self::Ipv6 => write!(f, "ipv6"),
930 Self::Any => write!(f, "any"),
931 }
932 }
933}
934
935#[cfg(any(test, feature = "arbitrary"))]
936impl Arbitrary for Afi {
937 type Parameters = ();
938 type Strategy = BoxedStrategy<Self>;
939 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
940 prop_oneof![Just(Self::Ipv4), Just(Self::Ipv6), Just(Self::Any)].boxed()
941 }
942}
943
944#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
949pub enum Safi {
950 Unicast,
952 Multicast,
954}
955
956impl TryFrom<TokenPair<'_>> for Safi {
957 type Error = ParseError;
958
959 fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
960 debug_construction!(pair => SafiName);
961 match pair.as_rule() {
962 ParserRule::safi_unicast => Ok(Self::Unicast),
963 ParserRule::safi_multicast => Ok(Self::Multicast),
964 _ => Err(rule_mismatch!(pair => "safi identifier")),
965 }
966 }
967}
968
969impl fmt::Display for Safi {
970 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
971 match self {
972 Self::Unicast => write!(f, "unicast"),
973 Self::Multicast => write!(f, "multicast"),
974 }
975 }
976}
977
978#[cfg(any(test, feature = "arbitrary"))]
979impl Arbitrary for Safi {
980 type Parameters = ();
981 type Strategy = BoxedStrategy<Self>;
982 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
983 prop_oneof![Just(Self::Unicast), Just(Self::Multicast)].boxed()
984 }
985}
986
987#[cfg(any(test, feature = "arbitrary"))]
989pub mod arbitrary {
990 use proptest::strategy::Strategy;
991
992 use regex::RegexSetBuilder;
993
994 #[allow(clippy::missing_panics_doc)]
997 pub fn prop_filter_keywords<S>(strategy: S) -> impl Strategy<Value = String>
998 where
999 S: Strategy<Value = String>,
1000 {
1001 let keywords = RegexSetBuilder::new([
1002 "^ANY$",
1003 "^AS-ANY$",
1004 "^RS-ANY$",
1005 "^PeerAS$",
1006 "^AND$",
1007 "^OR$",
1008 "^NOT$",
1009 "^ATOMIC$",
1010 "^FROM$",
1011 "^TO$",
1012 "^AT$",
1013 "^ACTION$",
1014 "^ACCEPT$",
1015 "^ANNOUNCE$",
1016 "^EXCEPT$",
1017 "^REFINE$",
1018 "^NETWORKS$",
1019 "^INTO$",
1020 "^INBOUND$",
1021 "^OUTBOUND$",
1022 ])
1023 .case_insensitive(true)
1024 .build()
1025 .expect("Unexpected regex compilation failure. Please file a bug.");
1026 strategy.prop_filter("names cannot collide with rpsl keywords", move |s| {
1027 !keywords.is_match(s)
1028 })
1029 }
1030
1031 macro_rules! impl_rpsl_name_arbitrary {
1032 ( $t:ty ) => {
1033 impl ::proptest::arbitrary::Arbitrary for $t {
1034 type Parameters = ();
1035 type Strategy = ::proptest::strategy::BoxedStrategy<Self>;
1036 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1037 let reserved =
1038 ::regex::Regex::new(r"^(?i)AS\d|AS-|RS-|FLTR-|RTRS-|PRNG-").unwrap();
1039 $crate::primitive::arbitrary::prop_filter_keywords("[A-Za-z][A-Za-z0-9_-]+")
1040 .prop_filter_map("names cannot begin with a reserved sequence", move |s| {
1041 if reserved.is_match(&s) {
1042 None
1043 } else {
1044 Some(Self(s))
1045 }
1046 })
1047 .boxed()
1048 }
1049 }
1050 };
1051 }
1052 pub(crate) use impl_rpsl_name_arbitrary;
1053
1054 macro_rules! impl_free_form_arbitrary {
1055 ( $t:ty ) => {
1056 impl ::proptest::arbitrary::Arbitrary for $t {
1057 type Parameters = ();
1058 type Strategy = ::proptest::strategy::BoxedStrategy<Self>;
1059 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1060 r"([^#\pC\s][^#\pC]*)?".prop_map(Self).boxed()
1061 }
1062 }
1063 };
1064 }
1065 pub(crate) use impl_free_form_arbitrary;
1066}