//! `pic apps domains {ls,add,rm}` — manage an app's domain claims. //! //! Host→app dispatch resolves an incoming `Host` to the app that claims //! it; a non-default app's routes are unreachable until it claims a //! domain. This is the CLI surface over the `apps/{id}/domains` admin API. //! //! Param-syntax note: domain patterns use `{name}` for the parameterized //! wildcard (e.g. `{tenant}.example.com`) — never `:name`, which is //! route-path syntax. use anyhow::Result; use crate::client::Client; use crate::config; use crate::output::{KvBlock, OutputMode, Table}; pub async fn ls(app: &str, mode: OutputMode) -> Result<()> { let creds = config::resolve()?; let client = Client::from_creds(&creds)?; let domains = client.domains_list(app).await?; let mut table = Table::new(["id", "pattern", "shape", "created_at"]); for d in domains { table.row([ d.id.to_string(), d.pattern.clone(), shape_label(d.shape).to_string(), d.created_at.to_rfc3339(), ]); } table.print(mode); Ok(()) } pub async fn add(app: &str, pattern: &str, mode: OutputMode) -> Result<()> { let creds = config::resolve()?; let client = Client::from_creds(&creds)?; let d = client.domains_create(app, pattern).await?; let mut block = KvBlock::new(); block .field("id", d.id.to_string()) .field("pattern", d.pattern.clone()) .field("shape", shape_label(d.shape).to_string()) .field("created_at", d.created_at.to_rfc3339()); block.print(mode); Ok(()) } pub async fn rm(app: &str, domain_id: &str) -> Result<()> { let creds = config::resolve()?; let client = Client::from_creds(&creds)?; client.domains_delete(app, domain_id).await?; println!("Deleted domain {domain_id}"); Ok(()) } fn shape_label(shape: picloud_shared::DomainShape) -> &'static str { match shape { picloud_shared::DomainShape::Exact => "exact", picloud_shared::DomainShape::Wildcard => "wildcard", picloud_shared::DomainShape::Parameterized => "parameterized", } }