use std::{error, fmt};
use std::str::FromStr;
use rpki::repository::resources::Asn;
use crate::cli::client::KrillClient;
use crate::cli::report::Report;
use crate::api;
use crate::commons::httpclient;
use super::ca;
#[derive(clap::Subcommand)]
pub enum Command {
List(List),
Add(Add),
Remove(Remove),
Update(Update),
}
impl Command {
pub async fn run(self, client: &KrillClient) -> Report {
match self {
Self::List(cmd) => cmd.run(client).await.into(),
Self::Add(cmd) => cmd.run(client).await.into(),
Self::Remove(cmd) => cmd.run(client).await.into(),
Self::Update(cmd) => cmd.run(client).await.into(),
}
}
}
#[derive(clap::Args)]
pub struct List {
#[command(flatten)]
ca: ca::Handle,
}
impl List {
pub async fn run(
self, client: &KrillClient
) -> Result<api::aspa::AspaDefinitionList, httpclient::Error> {
client.aspas_list(&self.ca.ca).await
}
}
#[derive(clap::Parser)]
pub struct Add {
#[command(flatten)]
ca: ca::Handle,
#[arg(long, value_name = "ASPA definition")]
aspa: CleanAspaDefinition,
}
impl Add {
async fn run(
self, client: &KrillClient
) -> Result<api::status::Success, httpclient::Error> {
client.aspas_update(
&self.ca.ca,
api::aspa::AspaDefinitionUpdates {
add_or_replace: vec![self.aspa.0],
remove: Vec::new(),
}
).await
}
}
#[derive(clap::Args)]
pub struct Remove {
#[command(flatten)]
ca: ca::Handle,
#[arg(long, value_name = "ASN")]
customer: Asn,
}
impl Remove {
async fn run(
self, client: &KrillClient
) -> Result<api::status::Success, httpclient::Error> {
client.aspas_update(
&self.ca.ca,
api::aspa::AspaDefinitionUpdates {
add_or_replace: Vec::new(),
remove: vec![self.customer]
},
).await
}
}
#[derive(clap::Args)]
pub struct Update {
#[command(flatten)]
ca: ca::Handle,
#[arg(long, value_name = "ASN")]
customer: Asn,
#[arg(long, value_name = "ASNn")]
add: Vec<Asn>,
#[arg(long, value_name = "ASNn")]
remove: Vec<Asn>,
}
impl Update {
pub async fn run(
self, client: &KrillClient
) -> Result<api::status::Success, httpclient::Error> {
client.aspas_update_single(
&self.ca.ca, self.customer,
api::aspa::AspaProvidersUpdate {
added: self.add,
removed: self.remove
},
).await
}
}
#[derive(Clone, Debug)]
pub struct CleanAspaDefinition(api::aspa::AspaDefinition);
impl FromStr for CleanAspaDefinition {
type Err = CleanAspaDefinitionError;
fn from_str(src: &str) -> Result<Self, Self::Err> {
let aspa = api::aspa::AspaDefinition::from_str(src).map_err(
CleanAspaDefinitionError::Format
)?;
if aspa.customer_used_as_provider() {
Err(CleanAspaDefinitionError::CustomerAsProvider)
}
else if aspa.providers.is_empty() {
Err(CleanAspaDefinitionError::EmptyProviders)
}
else {
Ok(Self(aspa))
}
}
}
#[derive(Debug)]
pub enum CleanAspaDefinitionError {
Format(api::aspa::AspaDefinitionFormatError),
CustomerAsProvider,
EmptyProviders,
}
impl fmt::Display for CleanAspaDefinitionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Format(err) => err.fmt(f),
Self::CustomerAsProvider => {
f.write_str("Customer ASN may not be used as provider.")
}
Self::EmptyProviders => {
f.write_str("At least one provider MUST be specified.")
}
}
}
}
impl error::Error for CleanAspaDefinitionError { }