1use std::{collections::BTreeSet, time::Duration};23use crate::{command::CommandExt, host::Config};4use anyhow::{bail, ensure, Result};5use clap::Parser;6use nixlike::format_nix;7use serde_json::{json, Value};8use tokio::{9 fs::{self, File},10 io::AsyncWriteExt,11 process::Command,12};1314#[derive(Parser)]15pub struct Info {16 #[clap(long)]17 json: bool,18 #[clap(subcommand)]19 cmd: InfoCmd,20}2122#[derive(Parser)]23pub enum InfoCmd {24 25 ListHosts {26 #[clap(long)]27 tagged: Vec<String>,28 },29 30 HostIps {31 host: String,32 #[clap(long)]33 external: bool,34 #[clap(long)]35 internal: bool,36 },37}3839impl Info {40 pub async fn run(self, config: &Config) -> Result<()> {41 let mut data = Vec::new();42 match self.cmd {43 InfoCmd::ListHosts { ref tagged } => {44 'host: for host in config.list_hosts().await? {45 if !tagged.is_empty() {46 let tags: Vec<String> = config.config_attr(&host, "tags").await?;47 for tag in tagged {48 if !tags.contains(tag) {49 continue 'host;50 }51 }52 }53 data.push(host);54 }55 }56 InfoCmd::HostIps {57 host,58 external,59 internal,60 } => {61 ensure!(62 external || internal,63 "at leas one of --external or --internal must be set"64 );65 let mut out = <BTreeSet<String>>::new();66 if external {67 out.extend(68 config69 .config_attr::<Vec<String>>(&host, "network.externalIps")70 .await?,71 );72 }73 if internal {74 out.extend(75 config76 .config_attr::<Vec<String>>(&host, "network.internalIps")77 .await?,78 );79 }80 for ip in out {81 data.push(ip);82 }83 }84 }8586 if self.json {87 let v = serde_json::to_string_pretty(&data)?;88 print!("{}", v);89 } else {90 for v in data {91 println!("{}", v);92 }93 }94 Ok(())95 }96}