use std::fmt;
use rust_i18n::t;
use tabled::Tabled;
use timeweb_rs::{apis::firewall_api, models as fw};
use crate::{error::TwcError, output::OutputFormat};
fn fmt_id(v: &str) -> String {
v.to_string()
}
#[derive(Tabled)]
struct GroupRow {
#[tabled(rename = "ID")]
id: String,
#[tabled(rename = "Name")]
name: String,
#[tabled(rename = "Policy")]
policy: String,
#[tabled(rename = "Created")]
created_at: String,
#[tabled(rename = "Updated")]
updated_at: String
}
impl fmt::Display for GroupRow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {} {} {}",
self.id, self.name, self.policy, self.created_at, self.updated_at
)
}
}
#[derive(Tabled)]
struct RuleRow {
#[tabled(rename = "ID")]
id: String,
#[tabled(rename = "Direction")]
direction: String,
#[tabled(rename = "Protocol")]
protocol: String,
#[tabled(rename = "Port")]
port: String,
#[tabled(rename = "Description")]
description: String
}
impl fmt::Display for RuleRow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {} {} {}",
self.id, self.direction, self.protocol, self.port, self.description
)
}
}
#[derive(Tabled)]
struct ResourceRow {
#[tabled(rename = "ID")]
id: String,
#[tabled(rename = "Type")]
r#type: String
}
impl fmt::Display for ResourceRow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.id, self.r#type)
}
}
pub async fn list(
config: &timeweb_rs::apis::configuration::Configuration,
limit: Option<i32>,
offset: Option<i32>,
format: OutputFormat
) -> Result<(), TwcError> {
let resp = firewall_api::get_groups(config, limit, offset).await?;
let rows: Vec<GroupRow> = resp
.groups
.iter()
.map(|g| GroupRow {
id: fmt_id(&g.id),
name: g.name.clone(),
policy: format!("{:?}", g.policy),
created_at: g
.created_at
.to_rfc3339_opts(chrono::SecondsFormat::Secs, false),
updated_at: g
.updated_at
.to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
})
.collect();
match format {
OutputFormat::Table => {
if rows.is_empty() {
println!("{}", t!("cli.no_firewall_groups"));
} else {
let table = crate::output::render_table(&rows);
println!("{table}");
}
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.groups)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
for g in &resp.groups {
println!("{}\t{}", fmt_id(&g.id), g.name);
}
}
}
Ok(())
}
pub async fn info(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
format: OutputFormat
) -> Result<(), TwcError> {
let resp = firewall_api::get_group(config, id).await?;
let g = &resp.group;
match format {
OutputFormat::Table => {
println!("ID: {}", fmt_id(&g.id));
println!("Name: {}", g.name);
println!("Description: {}", g.description);
println!("Policy: {:?}", g.policy);
println!(
"Created at: {}",
g.created_at
.to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
);
println!(
"Updated at: {}",
g.updated_at
.to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
);
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.group)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
println!("{}\t{}\t{:?}", fmt_id(&g.id), g.name, g.policy);
}
}
Ok(())
}
pub async fn create(
config: &timeweb_rs::apis::configuration::Configuration,
name: &str,
format: OutputFormat
) -> Result<(), TwcError> {
let req = fw::FirewallGroupInApi::new(name.to_string());
let resp = firewall_api::create_group(config, req, None).await?;
let g = &resp.group;
match format {
OutputFormat::Table => {
println!(
"{}",
t!("cli.firewall_group_created", name => g.name, id => fmt_id(&g.id))
);
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.group)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
println!("{}\t{}", fmt_id(&g.id), g.name);
}
}
Ok(())
}
pub async fn delete(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str
) -> Result<(), TwcError> {
firewall_api::delete_group(config, id).await?;
println!("{}", t!("cli.firewall_group_deleted", id => id));
Ok(())
}
pub async fn update(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
name: Option<&str>,
format: OutputFormat
) -> Result<(), TwcError> {
let mut update = fw::FirewallGroupInApi::new(String::new());
if let Some(n) = name {
update.name = n.to_string();
}
let resp = firewall_api::update_group(config, id, update).await?;
let g = &resp.group;
match format {
OutputFormat::Table => {
println!(
"{}",
t!("cli.firewall_group_updated", name => g.name, id => fmt_id(&g.id))
);
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.group)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
println!("{}\t{}", fmt_id(&g.id), g.name);
}
}
Ok(())
}
pub async fn rule_list(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
format: OutputFormat
) -> Result<(), TwcError> {
let resp = firewall_api::get_group_rules(config, id, None, None).await?;
let rows: Vec<RuleRow> = resp
.rules
.iter()
.map(|r| RuleRow {
id: fmt_id(&r.id),
direction: format!("{:?}", r.direction),
protocol: format!("{:?}", r.protocol),
port: r.port.clone().unwrap_or_else(|| String::from("*")),
description: r.description.clone()
})
.collect();
match format {
OutputFormat::Table => {
if rows.is_empty() {
println!("{}", t!("cli.no_rules"));
} else {
let table = crate::output::render_table(&rows);
println!("{table}");
}
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.rules)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
for r in &resp.rules {
let dir = format!("{:?}", r.direction);
let proto = format!("{:?}", r.protocol);
println!("{}\t{}\t{}", fmt_id(&r.id), dir, proto);
}
}
}
Ok(())
}
pub async fn rule_create(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
format: OutputFormat
) -> Result<(), TwcError> {
let req = fw::FirewallRuleInApi::new(
fw::FirewallRuleDirection::Ingress,
fw::FirewallRuleProtocol::Tcp
);
let resp = firewall_api::create_group_rule(config, id, req).await?;
let r = &resp.rule;
match format {
OutputFormat::Table => {
let dir_str = format!("{:?}", r.direction);
let proto_str = format!("{:?}", r.protocol);
let port_str = r.port.as_deref().unwrap_or("*");
println!(
"{}",
t!(
"cli.firewall_rule_created",
id => id,
rule_id => fmt_id(&r.id),
dir => dir_str,
proto => proto_str,
port => port_str
)
);
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.rule)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
println!("{}\t{}", fmt_id(&r.id), r.direction);
}
}
Ok(())
}
pub async fn rule_delete(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
rule_id: &str
) -> Result<(), TwcError> {
firewall_api::delete_group_rule(config, id, rule_id).await?;
println!(
"{}",
t!("cli.firewall_rule_deleted", rule_id => rule_id, id => id)
);
Ok(())
}
pub async fn resource_list(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
format: OutputFormat
) -> Result<(), TwcError> {
let resp = firewall_api::get_group_resources(config, id, None, None).await?;
let rows: Vec<ResourceRow> = resp
.resources
.iter()
.map(|r| ResourceRow {
id: r.id.to_string(),
r#type: format!("{:?}", r.r#type)
})
.collect();
match format {
OutputFormat::Table => {
if rows.is_empty() {
println!("{}", t!("cli.no_firewall_resources"));
} else {
let table = crate::output::render_table(&rows);
println!("{table}");
}
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.resources)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
for r in &resp.resources {
let rtype = format!("{:?}", r.r#type);
println!("{}\t{}", r.id, rtype);
}
}
}
Ok(())
}
pub async fn resource_add(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
resource_id: &str
) -> Result<(), TwcError> {
firewall_api::add_resource_to_group(config, id, resource_id, None).await?;
println!(
"{}",
t!("cli.firewall_resource_added", resource_id => resource_id, id => id)
);
Ok(())
}
pub async fn resource_remove(
config: &timeweb_rs::apis::configuration::Configuration,
id: &str,
resource_id: &str
) -> Result<(), TwcError> {
firewall_api::delete_resource_from_group(config, id, resource_id, None).await?;
println!(
"{}",
t!("cli.firewall_resource_removed", resource_id => resource_id, id => id)
);
Ok(())
}