From fbef11dfe7fa755e042484f7175904f8576aec3d Mon Sep 17 00:00:00 2001 From: MCorange Date: Tue, 7 Jul 2026 17:19:25 +0300 Subject: [PATCH] Owo --- src/config/cli.rs | 31 +++++++++++++++++++++++++++++-- src/config/mod.rs | 5 +++++ src/main.rs | 23 ++++++++++++++++++++--- src/mt_commander/mod.rs | 25 ++++++++++++++++++++----- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/config/cli.rs b/src/config/cli.rs index 1fa4f7a..824c2d5 100644 --- a/src/config/cli.rs +++ b/src/config/cli.rs @@ -7,6 +7,8 @@ * permission. */ +use std::net::IpAddr; + use clap::ArgGroup; use serde::de; @@ -25,9 +27,13 @@ pub struct CliArgs { pub config_file: camino::Utf8PathBuf, /// Output more information in logs - #[arg(long = "verbose", short = 'v')] + #[arg(long = "verbose", short = 'v', conflicts_with="quiet")] pub verbose: bool, + /// Output more information in logs + #[arg(long = "quiet", short = 'q', conflicts_with="verbose")] + pub quiet: bool, + #[command(subcommand)] pub command: CliCommand, } @@ -96,7 +102,28 @@ pub enum CliCommand { pub enum CliDeviceCommand { /// List devices List, - + Add { + /// Device name + #[arg(short='n', long)] + name: String, + /// Device address, ipv4 or ipv6 + #[arg(short='H', long)] + host: IpAddr, + /// Device Port (skip for default) + #[arg(short='p', long)] + port: Option, + /// Username (skip for default) + #[arg(short='U', long)] + username: Option, + /// Password (skip for default) + #[arg(short='P', long)] + password: Option + }, + Remove { + /// Name of the device + #[arg(short, long)] + name: String, + }, #[default] #[clap(skip)] None, diff --git a/src/config/mod.rs b/src/config/mod.rs index 711d693..b337e57 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -71,6 +71,11 @@ impl Config { slf.cli = cli; Ok(slf) } + pub fn save(&self) -> anyhow::Result<()> { + let data = toml::to_string_pretty(self)?; + std::fs::write(&self.cli.config_file, data)?; + Ok(()) + } } impl ConfigHost { diff --git a/src/main.rs b/src/main.rs index 123e012..abea903 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,19 +29,21 @@ mod mt_commander; #[tokio::main] async fn main() -> anyhow::Result<()> { - let cfg = config::Config::parse()?; + let mut cfg = config::Config::parse()?; //dbg!(&cfg); env_logger::builder() - .parse_default_env() .filter_module( "wara", if cfg.cli.verbose { LevelFilter::Debug + } else if cfg.cli.quiet { + LevelFilter::Error } else { LevelFilter::Info }, ) + .parse_default_env() .init(); mt_commander::MtCommander::init(cfg.clone())?; @@ -71,12 +73,27 @@ async fn main() -> anyhow::Result<()> { tokio::time::sleep(Duration::from_secs_f32(*interval)).await; } } - CliCommand::Device { command } => match command { + CliCommand::Device { command } => match command.clone() { CliDeviceCommand::List => { let devices = cfg.hosts.iter().map(|h| h.display_tabled(&cfg)); let table = tabled::Table::new(devices); println!("{}", table); + }, + CliDeviceCommand::Add { name, host, port, username, password } => { + cfg.hosts.push(ConfigHost { name, ip: host, port, username, password }); + cfg.save()?; + } + CliDeviceCommand::Remove { name } => { + for (i, host) in cfg.hosts.iter().enumerate() { + if host.name == name { + cfg.hosts.remove(i); + cfg.save()?; + return Ok(()); + } + } + log::error!("Unable to find host with name '{name}' to remove"); + return Ok(()); } CliDeviceCommand::None => unreachable!(), }, diff --git a/src/mt_commander/mod.rs b/src/mt_commander/mod.rs index f06253d..65486ad 100644 --- a/src/mt_commander/mod.rs +++ b/src/mt_commander/mod.rs @@ -14,6 +14,7 @@ use std::{ sync::{Arc, Mutex}, }; +use anyhow::bail; use mikrotik_rs::{Command, CommandBuilder, Event, MikrotikDevice}; use regex::Regex; @@ -112,23 +113,36 @@ impl MtCommanderInternal { host: &ConfigHost, cmd: &str, ) -> anyhow::Result>>> { - let dev = MikrotikDevice::connect( - format!("{}:{}", &host.ip, host.port(&self.cfg())), + let addr = format!("{}:{}", &host.ip, host.port(&self.cfg())); + let dev_res = MikrotikDevice::connect( + &addr, &host.username(self.cfg()), Some(&host.password(self.cfg())), ) - .await?; + .await; + + let dev = match dev_res { + Ok(dev) => { + log::info!("({addr}) Connected successfully!"); + dev + }, + Err(e) => { + log::error!("Failed to connect to {addr}: {e}"); + bail!(e); + } + }; let mut rx = dev .send_command(CommandBuilder::new().command(cmd).build()) .await?; let mut res = Vec::new(); - + let mut size = 0; while let Some(event) = rx.recv().await { match event { - Event::Reply { response, .. } => { + Event::Reply { response, tag: _ } => { log::debug!("({}) {:?}", &host.ip, response.attributes); + size += response.attributes.values().flatten().map(|v| v.len()).sum::(); res.push(response.attributes); } Event::Done { .. } => { @@ -140,6 +154,7 @@ impl MtCommanderInternal { other => println!("{other:?}"), } } + log::info!("({addr}) Command finished, read {size} bytes"); Ok(res) } }