diff --git a/rups/Cargo.toml b/rups/Cargo.toml index 8d729ee..d61cd6c 100644 --- a/rups/Cargo.toml +++ b/rups/Cargo.toml @@ -26,6 +26,7 @@ default = [] ssl = ["rustls", "rustls/dangerous_configuration", "webpki", "webpki-roots"] async = ["tokio"] async-ssl = ["async", "tokio-rustls", "ssl"] +write = [] # a feature gate for examples async-rt = ["async", "tokio/rt-multi-thread", "tokio/macros"] diff --git a/rups/src/cmd.rs b/rups/src/cmd.rs index c31a024..f222ac4 100644 --- a/rups/src/cmd.rs +++ b/rups/src/cmd.rs @@ -18,6 +18,9 @@ pub enum Command<'a> { NetworkVersion, /// Queries the server version. Version, + #[cfg(feature = "write")] + /// Run a command. Allow for on additional optional param. + Run(&'a str, Option<&'a str>), /// Gracefully shuts down the connection. Logout, } @@ -33,6 +36,8 @@ impl<'a> Command<'a> { Self::StartTLS => "STARTTLS", Self::NetworkVersion => "NETVER", Self::Version => "VER", + #[cfg(feature = "write")] + Self::Run(_, _) => "INSTCMD", Self::Logout => "LOGOUT", } } @@ -44,6 +49,10 @@ impl<'a> Command<'a> { Self::SetUsername(username) => vec![username], Self::SetPassword(password) => vec![password], Self::List(query) => query.to_vec(), + #[cfg(feature = "write")] + Self::Run(cmd, param) => param + .map(|param| vec![*cmd, param]) + .unwrap_or_else(|| vec![cmd]), _ => Vec::new(), } } @@ -826,7 +835,7 @@ implement_simple_commands! { pub fn get_network_version() -> String { ( { Command::NetworkVersion }, - { |row: String| Ok(row) }, + { Ok }, ) } @@ -834,7 +843,7 @@ implement_simple_commands! { pub fn get_server_version() -> String { ( { Command::Version }, - { |row: String| Ok(row) }, + { Ok }, ) } } @@ -855,3 +864,31 @@ implement_action_commands! { Command::Logout } } + +#[cfg(feature = "write")] +impl crate::blocking::Connection { + /// Runs a command on the UPS. + pub fn run_command(&mut self, cmd: &str, param: Option<&str>) -> crate::Result<()> { + match self { + Self::Tcp(conn) => { + conn.write_cmd(Command::Run(cmd, param))?; + conn.read_response()?.expect_ok()?; + Ok(()) + } + } + } +} + +#[cfg(all(feature = "write", feature = "async"))] +impl crate::tokio::Connection { + /// Runs a command on the UPS. + pub async fn run_command(&mut self, cmd: &str, param: Option<&str>) -> crate::Result<()> { + match self { + Self::Tcp(conn) => { + conn.write_cmd(Command::Run(cmd, param)).await?; + conn.read_response().await?.expect_ok()?; + Ok(()) + } + } + } +} diff --git a/rups/src/proto/mod.rs b/rups/src/proto/mod.rs index a1d5c6e..89d6a58 100644 --- a/rups/src/proto/mod.rs +++ b/rups/src/proto/mod.rs @@ -213,6 +213,9 @@ macro_rules! impl_sentences { /// 2. the decoded sentence /// /// ``` +/// # #[macro_use] extern crate rups; +/// # fn main() { +/// # #[cfg(test)] /// test_encode_decode!( /// ["GET", "VAR", "nutdev", "test.var"] <=> /// Sentences::QueryVar { @@ -220,6 +223,7 @@ macro_rules! impl_sentences { /// var_name: "test.var".into(), /// } /// ); +/// # } /// ``` #[allow(unused_macros)] macro_rules! test_encode_decode { diff --git a/rups/src/var.rs b/rups/src/var.rs index 84e0a24..cdfea10 100644 --- a/rups/src/var.rs +++ b/rups/src/var.rs @@ -5,7 +5,7 @@ use std::time::Duration; /// Well-known variable keys for NUT UPS devices. /// -/// List retrieved from: https://networkupstools.org/docs/user-manual.chunked/apcs01.html +/// List retrieved from: pub mod key { /// Device model. pub const DEVICE_MODEL: &str = "device.model"; @@ -31,7 +31,7 @@ pub mod key { /// Well-known variables for NUT UPS devices. /// -/// List retrieved from: https://networkupstools.org/docs/user-manual.chunked/apcs01.html +/// List retrieved from: #[derive(Debug, Clone, Eq, PartialEq)] pub enum Variable { /// Device model. @@ -196,10 +196,8 @@ impl TryFrom<&str> for VariableType { other => { if other.starts_with("STRING:") { let size = other - .splitn(2, ':') - .nth(1) - .map(|s| s.parse().ok()) - .flatten() + .split_once(':') + .and_then(|(_, s)| s.parse().ok()) .ok_or_else(|| crate::ClientError::generic("Invalid STRING definition"))?; Ok(Self::String(size)) } else {