use crate::identifier::{asn::Asn, isd::Isd, isd_asn::IsdAsn};
#[derive(Debug)]
pub struct ListSegmentPlan {
pub up: Option<(IsdAsn, IsdAsn)>,
pub core: Option<(IsdAsn, IsdAsn)>,
pub down: Option<(IsdAsn, IsdAsn)>,
}
#[derive(Debug, thiserror::Error)]
pub enum ListSegmentPlanError {
#[error("source and destination AS must differ in path segment lookup")]
SameSourceAndDestination,
#[error("at least one segment lookup must be specified")]
NoSegmentLookup,
#[error("up segment lookup must be in same ISD: {0} != {1}")]
UpSegmentIsdMismatch(Isd, Isd),
#[error("up segment lookup source must not be wildcard: {0}")]
UpSegmentSourceWildcard(IsdAsn),
#[error("down segment lookup must be in same ISD: {0} != {1}")]
DownSegmentIsdMismatch(Isd, Isd),
#[error("down segment lookup destination must not be wildcard: {0}")]
DownSegmentDestinationWildcard(IsdAsn),
}
impl ListSegmentPlan {
pub fn new(
src: Src,
src_cores: CoreHint,
dst: Dst,
) -> Result<ListSegmentPlan, ListSegmentPlanError> {
if src.ias() == dst.ias() {
return Err(ListSegmentPlanError::SameSourceAndDestination);
}
let same_isd = src.isd() == dst.isd();
match same_isd {
true => Self::plan_same_isd(src, src_cores, dst),
false => Self::plan_cross_isd(src, dst),
}
}
fn plan_same_isd(
src: Src,
src_cores: CoreHint,
dst: Dst,
) -> Result<ListSegmentPlan, ListSegmentPlanError> {
let any_core = IsdAsn::new(src.isd(), Asn::WILDCARD);
match src_cores {
CoreHint::Single(single_core) => {
match (src, dst) {
(Src::Core(_), Dst::Core(_)) | (Src::Core(_), Dst::AnyCore(_)) => Self::none(),
(Src::Core(src), Dst::NonCore(dst)) => Self::down(src, dst),
(Src::NonCore(src), Dst::Core(dst)) => Self::up(src, dst),
(Src::NonCore(src), Dst::AnyCore(dst)) => Self::up(src, dst),
(Src::NonCore(src), Dst::NonCore(dst)) => Self::up_down(src, single_core, dst),
}
}
CoreHint::Multiple => {
match (src, dst) {
(Src::Core(src), Dst::Core(dst)) => Self::core(src, dst),
(Src::Core(src), Dst::AnyCore(dst)) => Self::core(src, dst),
(Src::Core(src), Dst::NonCore(dst)) => Self::core_down(src, any_core, dst),
(Src::NonCore(src), Dst::AnyCore(dst)) => Self::up(src, dst),
(Src::NonCore(src), Dst::Core(dst)) => Self::up_core(src, any_core, dst),
(Src::NonCore(src), Dst::NonCore(dst)) => {
Self::up_core_down(src, any_core, any_core, dst)
}
}
}
}
}
fn plan_cross_isd(src: Src, dst: Dst) -> Result<ListSegmentPlan, ListSegmentPlanError> {
let src_any_core = IsdAsn::new(src.isd(), Asn::WILDCARD);
let dst_any_core = IsdAsn::new(dst.isd(), Asn::WILDCARD);
match (src, dst) {
(Src::Core(src), Dst::Core(dst)) => Self::core(src, dst),
(Src::Core(src), Dst::AnyCore(dst)) => Self::core(src, dst),
(Src::Core(src), Dst::NonCore(dst)) => Self::core_down(src, dst_any_core, dst),
(Src::NonCore(src), Dst::AnyCore(dst)) => Self::up_core(src, src_any_core, dst),
(Src::NonCore(src), Dst::Core(dst)) => Self::up_core(src, src_any_core, dst),
(Src::NonCore(src), Dst::NonCore(dst)) => {
Self::up_core_down(src, src_any_core, dst_any_core, dst)
}
}
}
}
impl ListSegmentPlan {
fn validate(self) -> Result<Self, ListSegmentPlanError> {
if self.up.is_none() && self.core.is_none() && self.down.is_none() {
return Err(ListSegmentPlanError::NoSegmentLookup);
}
if let Some((src, dst)) = &self.up {
if src.isd() != dst.isd() {
return Err(ListSegmentPlanError::UpSegmentIsdMismatch(
src.isd(),
dst.isd(),
));
}
if src.is_wildcard() {
return Err(ListSegmentPlanError::UpSegmentSourceWildcard(*src));
}
}
if let Some((src, dst)) = &self.down {
if src.isd() != dst.isd() {
return Err(ListSegmentPlanError::DownSegmentIsdMismatch(
src.isd(),
dst.isd(),
));
}
if dst.is_wildcard() {
return Err(ListSegmentPlanError::DownSegmentDestinationWildcard(*src));
}
}
Ok(self)
}
pub fn none() -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: None,
core: None,
down: None,
}
.validate()
}
pub fn core(src: IsdAsn, dst: IsdAsn) -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: None,
core: Some((src, dst)),
down: None,
}
.validate()
}
pub fn up(src: IsdAsn, dst: IsdAsn) -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: Some((src, dst)),
core: None,
down: None,
}
.validate()
}
pub fn down(src: IsdAsn, dst: IsdAsn) -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: None,
core: None,
down: Some((src, dst)),
}
.validate()
}
pub fn up_core(
src: IsdAsn,
transit: IsdAsn,
dst: IsdAsn,
) -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: Some((src, transit)),
core: Some((transit, dst)),
down: None,
}
.validate()
}
pub fn core_down(
src: IsdAsn,
transit: IsdAsn,
dst: IsdAsn,
) -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: None,
core: Some((src, transit)),
down: Some((transit, dst)),
}
.validate()
}
pub fn up_down(
src: IsdAsn,
transit: IsdAsn,
dst: IsdAsn,
) -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: Some((src, transit)),
core: None,
down: Some((transit, dst)),
}
.validate()
}
pub fn up_core_down(
src: IsdAsn,
src_transit: IsdAsn,
dst_transit: IsdAsn,
dst: IsdAsn,
) -> Result<ListSegmentPlan, ListSegmentPlanError> {
ListSegmentPlan {
up: Some((src, src_transit)),
core: Some((src_transit, dst_transit)),
down: Some((dst_transit, dst)),
}
.validate()
}
}
pub enum Dst {
Core(IsdAsn),
NonCore(IsdAsn),
AnyCore(IsdAsn),
}
impl Dst {
pub fn new(ias: IsdAsn, is_core: bool) -> Self {
match ias.is_wildcard() {
true => Dst::AnyCore(ias),
false if is_core => Dst::Core(ias),
false => Dst::NonCore(ias),
}
}
pub fn ias(&self) -> IsdAsn {
match self {
Dst::Core(ias) => *ias,
Dst::NonCore(ias) => *ias,
Dst::AnyCore(isd) => IsdAsn::new(isd.isd(), Asn::WILDCARD),
}
}
pub fn isd(&self) -> Isd {
match self {
Dst::Core(ias) => ias.isd(),
Dst::NonCore(ias) => ias.isd(),
Dst::AnyCore(ias) => ias.isd(),
}
}
}
pub enum Src {
Core(IsdAsn),
NonCore(IsdAsn),
}
#[derive(Debug, thiserror::Error)]
#[error("source AS must not be wildcard")]
pub struct SrcWildcardError;
impl Src {
pub fn new(ias: IsdAsn, is_core: bool) -> Result<Self, SrcWildcardError> {
match ias.is_wildcard() {
true => Err(SrcWildcardError),
false if is_core => Ok(Src::Core(ias)),
false => Ok(Src::NonCore(ias)),
}
}
pub fn ias(&self) -> IsdAsn {
match self {
Src::Core(ias) => *ias,
Src::NonCore(ias) => *ias,
}
}
pub fn isd(&self) -> Isd {
match self {
Src::Core(ias) => ias.isd(),
Src::NonCore(ias) => ias.isd(),
}
}
}
pub enum CoreHint {
Single(IsdAsn),
Multiple,
}