use std::collections::HashMap;
use std::fmt;
use crate::{
attr::{AttributeSeq, AttributeType, RpslAttribute},
error::{ParseError, ParseResult, ValidationError, ValidationResult},
names,
parser::{debug_construction, impl_from_str, rule_mismatch, ParserRule, TokenPair},
};
mod macros;
use self::macros::rpsl_object_class;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum RpslObject {
Mntner(Mntner),
Person(Person),
Role(Role),
KeyCert(KeyCert),
AsBlock(AsBlock),
AutNum(AutNum),
InetNum(InetNum),
Inet6Num(Inet6Num),
Route(Route),
Route6(Route6),
AsSet(AsSet),
RouteSet(RouteSet),
FilterSet(FilterSet),
RtrSet(RtrSet),
PeeringSet(PeeringSet),
InetRtr(InetRtr),
Dictionary(Dictionary),
}
impl TryFrom<TokenPair<'_>> for RpslObject {
type Error = ParseError;
fn try_from(pair: TokenPair<'_>) -> ParseResult<Self> {
debug_construction!(pair => RpslObject);
match pair.as_rule() {
ParserRule::mntner_obj => Ok(Self::Mntner(pair.try_into()?)),
ParserRule::person_obj => Ok(Self::Person(pair.try_into()?)),
ParserRule::role_obj => Ok(Self::Role(pair.try_into()?)),
ParserRule::key_cert_obj => Ok(Self::KeyCert(pair.try_into()?)),
ParserRule::as_block_obj => Ok(Self::AsBlock(pair.try_into()?)),
ParserRule::aut_num_obj => Ok(Self::AutNum(pair.try_into()?)),
ParserRule::inetnum_obj => Ok(Self::InetNum(pair.try_into()?)),
ParserRule::inet6num_obj => Ok(Self::Inet6Num(pair.try_into()?)),
ParserRule::route_obj => Ok(Self::Route(pair.try_into()?)),
ParserRule::route6_obj => Ok(Self::Route6(pair.try_into()?)),
ParserRule::as_set_obj => Ok(Self::AsSet(pair.try_into()?)),
ParserRule::route_set_obj => Ok(Self::RouteSet(pair.try_into()?)),
ParserRule::filter_set_obj => Ok(Self::FilterSet(pair.try_into()?)),
ParserRule::rtr_set_obj => Ok(Self::RtrSet(pair.try_into()?)),
ParserRule::peering_set_obj => Ok(Self::PeeringSet(pair.try_into()?)),
ParserRule::inet_rtr_obj => Ok(Self::InetRtr(pair.try_into()?)),
ParserRule::dictionary_obj => Ok(Self::Dictionary(pair.try_into()?)),
_ => Err(rule_mismatch!(pair => "rpsl object")),
}
}
}
impl_from_str!(ParserRule::just_rpsl_object => RpslObject);
impl fmt::Display for RpslObject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Mntner(obj) => obj.fmt(f),
Self::Person(obj) => obj.fmt(f),
Self::Role(obj) => obj.fmt(f),
Self::KeyCert(obj) => obj.fmt(f),
Self::AsBlock(obj) => obj.fmt(f),
Self::AutNum(obj) => obj.fmt(f),
Self::InetNum(obj) => obj.fmt(f),
Self::Inet6Num(obj) => obj.fmt(f),
Self::Route(obj) => obj.fmt(f),
Self::Route6(obj) => obj.fmt(f),
Self::AsSet(obj) => obj.fmt(f),
Self::RouteSet(obj) => obj.fmt(f),
Self::FilterSet(obj) => obj.fmt(f),
Self::RtrSet(obj) => obj.fmt(f),
Self::PeeringSet(obj) => obj.fmt(f),
Self::InetRtr(obj) => obj.fmt(f),
Self::Dictionary(obj) => obj.fmt(f),
}
}
}
#[cfg(any(test, feature = "arbitrary"))]
mod arbitrary {
use proptest::{
arbitrary::{any, Arbitrary},
prop_oneof,
strategy::{BoxedStrategy, Strategy as _},
};
use super::{
AsBlock, AsSet, AutNum, Dictionary, FilterSet, Inet6Num, InetNum, InetRtr, KeyCert, Mntner,
PeeringSet, Person, Role, Route, Route6, RouteSet, RpslObject, RtrSet,
};
impl Arbitrary for RpslObject {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
prop_oneof![
any::<Mntner>().prop_map(Self::Mntner),
any::<Person>().prop_map(Self::Person),
any::<Role>().prop_map(Self::Role),
any::<KeyCert>().prop_map(Self::KeyCert),
any::<AsBlock>().prop_map(Self::AsBlock),
any::<AutNum>().prop_map(Self::AutNum),
any::<InetNum>().prop_map(Self::InetNum),
any::<Inet6Num>().prop_map(Self::Inet6Num),
any::<Route>().prop_map(Self::Route),
any::<Route6>().prop_map(Self::Route6),
any::<AsSet>().prop_map(Self::AsSet),
any::<RouteSet>().prop_map(Self::RouteSet),
any::<FilterSet>().prop_map(Self::FilterSet),
any::<RtrSet>().prop_map(Self::RtrSet),
any::<PeeringSet>().prop_map(Self::PeeringSet),
any::<InetRtr>().prop_map(Self::InetRtr),
any::<Dictionary>().prop_map(Self::Dictionary),
]
.boxed()
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct AttributeRule {
attr: AttributeType,
mandatory: bool,
multivalued: bool,
}
impl AttributeRule {
const fn new(attr: AttributeType, mandatory: bool, multivalued: bool) -> Self {
Self {
attr,
mandatory,
multivalued,
}
}
}
pub trait RpslObjectClass: Sized {
const CLASS: &'static str;
const ATTRS: &'static [AttributeRule];
type Name;
fn new<I>(name: Self::Name, iter: I) -> ValidationResult<Self>
where
I: IntoIterator<Item = RpslAttribute>;
fn name(&self) -> &Self::Name;
fn attrs(&self) -> &AttributeSeq;
fn validate<I>(attrs: I) -> ValidationResult<AttributeSeq>
where
I: IntoIterator<Item = RpslAttribute>,
{
let mut seen: HashMap<AttributeType, usize> = HashMap::new();
let allowed: HashMap<AttributeType, bool> = Self::ATTRS
.iter()
.map(|rule| (rule.attr, rule.multivalued))
.collect();
let seq = attrs
.into_iter()
.inspect(|attr| {
let count = seen.entry(attr.into()).or_insert(0);
*count += 1;
})
.collect();
seen.iter().try_for_each(|(attr, count)| {
allowed
.get(attr)
.ok_or_else::<ValidationError, _>(|| {
format!(
"attribute '{}' not allowed in '{}' object",
attr,
Self::CLASS
)
.into()
})
.and_then(|multivalued| {
if !multivalued && count > &1 {
Err(format!(
"multiple '{}' attributes not allowed in '{}' object",
attr,
Self::CLASS
)
.into())
} else {
Ok(())
}
})
})?;
Self::ATTRS
.iter()
.filter(|rule| rule.mandatory)
.try_for_each(|rule| {
seen.contains_key(&rule.attr)
.then_some(())
.ok_or_else::<ValidationError, _>(|| {
format!(
"missing mandatory attribute {} in '{}' object",
rule.attr,
Self::CLASS
)
.into()
})
})?;
Ok(seq)
}
}
rpsl_object_class! {
Mntner {
class: "mntner",
name: names::Mntner,
parser_rule: ParserRule::mntner_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
Auth (+),
UpdTo (+),
MntNfy (*),
ReferralBy (*),
AuthOverride (?),
],
}
}
rpsl_object_class! {
Person {
class: "person",
name: names::Person,
parser_rule: ParserRule::person_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
NicHdl,
Address (+),
Phone (+),
FaxNo (*),
EMail (+),
Auth (*),
],
}
}
rpsl_object_class! {
Role {
class: "role",
name: names::Role,
parser_rule: ParserRule::role_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
NicHdl,
Trouble (*),
Address (+),
Phone (+),
FaxNo (*),
EMail (+),
Auth (*),
],
}
}
rpsl_object_class! {
KeyCert {
class: "key-cert",
name: names::KeyCert,
parser_rule: ParserRule::key_cert_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
Method,
Owner (+),
Fingerpr,
Certif,
],
}
}
rpsl_object_class! {
AsBlock {
class: "as-block",
name: names::AsBlock,
parser_rule: ParserRule::as_block_obj,
attributes: [
Descr (*),
AdminC (*),
TechC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
MntLower (*),
Reclaim (?),
NoReclaim (?),
],
}
}
rpsl_object_class! {
AutNum {
class: "aut-num",
name: names::AutNum,
parser_rule: ParserRule::aut_num_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
AsName,
AutNumMemberOf (*),
Import (*),
MpImport (*),
Export (*),
MpExport (*),
Default (*),
MpDefault (*),
MntRoutes (*),
MntLower (*),
Reclaim (?),
NoReclaim (?),
],
}
}
rpsl_object_class! {
InetNum {
class: "inetnum",
name: names::InetNum,
parser_rule: ParserRule::inetnum_obj,
attributes: [
Netname,
Descr (*),
Country (+),
AdminC (*),
TechC (*),
MntBy (+),
Changed (+),
Source,
MntRoutes (*),
MntLower (*),
Reclaim (?),
NoReclaim (?),
],
}
}
rpsl_object_class! {
Inet6Num {
class: "inet6num",
name: names::Inet6Num,
parser_rule: ParserRule::inet6num_obj,
attributes: [
Netname,
Descr (*),
Country (+),
AdminC (*),
TechC (*),
MntBy (+),
Changed (+),
Source,
MntRoutes (*),
MntLower (*),
Reclaim (?),
NoReclaim (?),
],
}
}
rpsl_object_class! {
Route {
class: "route",
name: names::Route,
parser_rule: ParserRule::route_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
Origin,
RouteMemberOf (*),
Inject (*),
Components (?),
AggrBndry (?),
AggrMtd (?),
ExportComps (?),
Holes (*),
MntRoutes (*),
MntLower (*),
Pingable4 (*),
PingHdl (*),
Reclaim (?),
NoReclaim (?),
],
}
}
rpsl_object_class! {
Route6 {
class: "route6",
name: names::Route6,
parser_rule: ParserRule::route6_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
Origin,
RouteMemberOf (*),
Inject6 (*),
Components6 (?),
AggrBndry (?),
AggrMtd (?),
ExportComps6 (?),
Holes6 (*),
MntRoutes (*),
MntLower (*),
Pingable6 (*),
PingHdl (*),
Reclaim (?),
NoReclaim (?),
],
}
}
rpsl_object_class! {
AsSet {
class: "as-set",
name: names::AsSet,
parser_rule: ParserRule::as_set_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
AsSetMembers (*),
MbrsByRef (*),
MntLower (*),
],
}
}
rpsl_object_class! {
RouteSet {
class: "route-set",
name: names::RouteSet,
parser_rule: ParserRule::route_set_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
RouteSetMembers (*),
RouteSetMpMembers (*),
MbrsByRef (*),
MntLower (*),
],
}
}
rpsl_object_class! {
FilterSet {
class: "filter-set",
name: names::FilterSet,
parser_rule: ParserRule::filter_set_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
Filter (?),
MpFilter (?),
MntLower (*),
],
}
}
rpsl_object_class! {
RtrSet {
class: "rtr-set",
name: names::RtrSet,
parser_rule: ParserRule::rtr_set_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
RtrSetMembers (*),
RtrSetMpMembers (*),
MbrsByRef (*),
MntLower (*),
],
}
}
rpsl_object_class! {
PeeringSet {
class: "peering-set",
name: names::PeeringSet,
parser_rule: ParserRule::peering_set_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
Peering (*),
MpPeering (*),
MntLower (*),
],
}
}
rpsl_object_class! {
InetRtr {
class: "inet-rtr",
name: names::InetRtr,
parser_rule: ParserRule::inet_rtr_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
Alias (*),
LocalAs,
Ifaddr (+),
Interface (*),
Peer (*),
MpPeer (*),
InetRtrMemberOf (*),
],
}
}
rpsl_object_class! {
Dictionary {
class: "dictionary",
name: names::Dictionary,
parser_rule: ParserRule::dictionary_obj,
attributes: [
Descr (*),
TechC (*),
AdminC (*),
Remarks (*),
Notify (*),
MntBy (+),
Changed (+),
Source,
],
}
}
#[cfg(test)]
mod tests;