use crate::api::APIErrorResponse; use crate::{NetworkCommands, TableStyle}; use comfy_table::modifiers::UTF8_ROUND_CORNERS; use comfy_table::presets::UTF8_FULL; use comfy_table::Table; use serde::Deserialize; use std::error::Error; use std::fs; use url::Url; pub async fn network_main(command: NetworkCommands, server: Url) -> Result<(), Box> { match command { NetworkCommands::List { table_style } => list_networks(server, table_style).await, NetworkCommands::Lookup { id } => get_network(id, server).await, } } #[derive(Deserialize)] pub struct NetworkListResp { pub data: Vec, } #[derive(Deserialize)] pub struct Network { pub id: String, pub cidr: String, #[serde(rename = "organizationID")] pub organization_id: String, #[serde(rename = "signingCAID")] pub signing_ca_id: String, #[serde(rename = "createdAt")] pub created_at: String, #[serde(rename = "lighthousesAsRelays")] pub lighthouses_as_relays: bool, pub name: String, } pub async fn list_networks(server: Url, table_style: TableStyle) -> Result<(), Box> { let client = reqwest::Client::new(); // load session token let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token"); let session_token = fs::read_to_string(&sess_token_store)?; let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token"); let auth_token = fs::read_to_string(&auth_token_store)?; let token = format!("{} {}", session_token, auth_token); let res = client .get(server.join("/v1/networks")?) .bearer_auth(token) .send() .await?; if res.status().is_success() { let resp: NetworkListResp = res.json().await?; if resp.data.is_empty() { println!("No networks found"); return Ok(()); } if matches!(table_style, TableStyle::List) { for network in &resp.data { println!(" Network: {}", network.id); println!(" CIDR: {}", network.cidr); println!(" Organization: {}", network.organization_id); println!(" Signing CA: {}", network.signing_ca_id); println!("Dedicated Relays: {}", !network.lighthouses_as_relays); println!(" Name: {}", network.name); println!(" Created At: {}", network.created_at); println!(); } return Ok(()); } let mut table = Table::new(); match table_style { TableStyle::List => unreachable!(), TableStyle::Basic => (), TableStyle::Pretty => { table .load_preset(UTF8_FULL) .apply_modifier(UTF8_ROUND_CORNERS); } }; table.set_header(vec![ "ID", "Name", "CIDR", "Organization ID", "Signing CA ID", "Dedicated Relays", "Created At", ]); for network in &resp.data { table.add_row(vec![ &network.id, &network.name, &network.cidr, &network.organization_id, &network.signing_ca_id, (!network.lighthouses_as_relays).to_string().as_str(), &network.created_at, ]); } println!("{table}"); } else { let resp: APIErrorResponse = res.json().await?; eprintln!( "[error] Error listing networks: {} {}", resp.errors[0].code, resp.errors[0].message ); std::process::exit(1); } Ok(()) } #[derive(Deserialize)] pub struct NetworkGetResponse { pub data: Network, } pub async fn get_network(id: String, server: Url) -> Result<(), Box> { let client = reqwest::Client::new(); // load session token let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token"); let session_token = fs::read_to_string(&sess_token_store)?; let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token"); let auth_token = fs::read_to_string(&auth_token_store)?; let token = format!("{} {}", session_token, auth_token); let res = client .get(server.join(&format!("/v1/networks/{}", id))?) .bearer_auth(token) .send() .await?; if res.status().is_success() { let network: Network = res.json::().await?.data; println!(" Network: {}", network.id); println!(" CIDR: {}", network.cidr); println!(" Organization: {}", network.organization_id); println!(" Signing CA: {}", network.signing_ca_id); println!("Dedicated Relays: {}", !network.lighthouses_as_relays); println!(" Name: {}", network.name); println!(" Created At: {}", network.created_at); } else { let resp: APIErrorResponse = res.json().await?; eprintln!( "[error] Error listing networks: {} {}", resp.errors[0].code, resp.errors[0].message ); std::process::exit(1); } Ok(()) }