use chrono::prelude::*; use structopt::StructOpt; use tailscale_api::*; #[derive(StructOpt, Debug)] struct Opt { /// The domain associated with your tailscale account. #[structopt(env = "TAILSCALE_DOMAIN", long)] domain: String, /// The tailscale API key as fetched from the admin panel. #[structopt(env = "TAILSCALE_API_KEY", long, hide_env_values = true)] api_key: String, #[structopt(subcommand)] command: Command, } #[derive(StructOpt, Debug)] enum Command { /// Prints or modifies the list of domains that Magic DNS uses. Nameservers { servers: Vec }, /// Prints or modifies the list of DNS search paths that Magic DNS uses. Searchpaths { domains: Vec }, /// Prints the list of devices in your tailscale network. Devices, } fn main() -> Result { let opt = Opt::from_args(); //println!("{:?}", opt); let cli = Client::new(opt.domain, opt.api_key, "Xe/ts".to_string()); use Command::*; match opt.command { Nameservers { servers } => match servers.len() { 0 => println!("{:?}", cli.get_nameservers()?), _ => println!("{:?}", cli.set_nameservers(servers)?), }, Searchpaths { domains } => match domains.len() { 0 => println!("{:?}", cli.get_search_paths()?), _ => println!("{:?}", cli.set_search_paths(domains)?), }, Devices => { let _ = cli .devices()? .devices .into_iter() .map(device_row) .for_each(|dev| println!("{}", dev)); } } Ok(()) } fn device_row(d: Device) -> String { format!( "{}, {}, {}, {}, {}", d.display_node_key, d.hostname, d.os, d.client_version, { let delta = Utc::now() - d.last_seen; format!( "last seen {} days {} hours {} minutes ago", delta.num_days(), delta.num_hours() % 24, delta.num_minutes() % 60 ) } ) }