mirror of
https://github.com/aramperes/nut-rs.git
synced 2025-09-08 05:08:31 -04:00
parent
cf0f058c7a
commit
f1700739f6
4 changed files with 222 additions and 2 deletions
|
@ -47,6 +47,7 @@ async fn main() -> nut_client::Result<()> {
|
|||
println!("\t Mutable Variables:");
|
||||
for var in mutable_vars.iter() {
|
||||
println!("\t\t- {}", var);
|
||||
println!("\t\t {:?}", conn.get_var_type(&name, var.name()).await?);
|
||||
}
|
||||
|
||||
// List UPS immutable properties (key = val)
|
||||
|
@ -56,6 +57,7 @@ async fn main() -> nut_client::Result<()> {
|
|||
continue;
|
||||
}
|
||||
println!("\t\t- {}", var);
|
||||
println!("\t\t {:?}", conn.get_var_type(&name, var.name()).await?);
|
||||
}
|
||||
|
||||
// List UPS commands
|
||||
|
|
|
@ -43,6 +43,7 @@ fn main() -> nut_client::Result<()> {
|
|||
println!("\t Mutable Variables:");
|
||||
for var in mutable_vars.iter() {
|
||||
println!("\t\t- {}", var);
|
||||
println!("\t\t {:?}", conn.get_var_type(&name, var.name())?);
|
||||
}
|
||||
|
||||
// List UPS immutable properties (key = val)
|
||||
|
@ -52,6 +53,7 @@ fn main() -> nut_client::Result<()> {
|
|||
continue;
|
||||
}
|
||||
println!("\t\t- {}", var);
|
||||
println!("\t\t {:?}", conn.get_var_type(&name, var.name())?);
|
||||
}
|
||||
|
||||
// List UPS commands
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use core::fmt;
|
||||
|
||||
use crate::{ClientError, NutError, Variable};
|
||||
use crate::{ClientError, NutError, Variable, VariableDefinition};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Command<'a> {
|
||||
|
@ -98,8 +99,12 @@ pub enum Response {
|
|||
Desc(String),
|
||||
/// A NUMLOGINS response.
|
||||
///
|
||||
/// Params (number of logins)
|
||||
/// Params: (number of logins)
|
||||
NumLogins(i32),
|
||||
/// A variable type (TYPE) response.
|
||||
///
|
||||
/// Params: (variable name, variable types)
|
||||
Type(String, Vec<String>),
|
||||
}
|
||||
|
||||
impl Response {
|
||||
|
@ -348,6 +353,24 @@ impl Response {
|
|||
})?;
|
||||
Ok(Response::NumLogins(num))
|
||||
}
|
||||
"TYPE" => {
|
||||
let _device = if args.is_empty() {
|
||||
Err(ClientError::from(NutError::Generic(
|
||||
"Unspecified TYPE device in response".into(),
|
||||
)))
|
||||
} else {
|
||||
Ok(args.remove(0))
|
||||
}?;
|
||||
let name = if args.is_empty() {
|
||||
Err(ClientError::from(NutError::Generic(
|
||||
"Unspecified TYPE name in response".into(),
|
||||
)))
|
||||
} else {
|
||||
Ok(args.remove(0))
|
||||
}?;
|
||||
let types = args;
|
||||
Ok(Response::Type(name, types))
|
||||
}
|
||||
_ => Err(NutError::UnknownResponseType(cmd_name).into()),
|
||||
}
|
||||
}
|
||||
|
@ -443,6 +466,17 @@ impl Response {
|
|||
Err(NutError::UnexpectedResponse.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_type(&self) -> crate::Result<VariableDefinition> {
|
||||
if let Self::Type(name, types) = &self {
|
||||
VariableDefinition::try_from((
|
||||
name.to_owned(),
|
||||
types.iter().map(String::as_str).collect(),
|
||||
))
|
||||
} else {
|
||||
Err(NutError::UnexpectedResponse.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A macro for implementing `LIST` commands.
|
||||
|
@ -701,6 +735,14 @@ implement_get_commands! {
|
|||
)
|
||||
}
|
||||
|
||||
/// Queries the type of a UPS variable.
|
||||
pub fn get_var_type(ups_name: &str, variable: &str) -> VariableDefinition {
|
||||
(
|
||||
{ &["TYPE", ups_name, variable] },
|
||||
{ |row: Response| row.expect_type() },
|
||||
)
|
||||
}
|
||||
|
||||
/// Queries the description of a UPS command.
|
||||
pub fn get_command_description(ups_name: &str, variable: &str) -> String {
|
||||
(
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use core::fmt;
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Well-known variable keys for NUT UPS devices.
|
||||
|
@ -165,3 +167,175 @@ impl fmt::Display for DeviceType {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// NUT Variable type
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum VariableType {
|
||||
/// A mutable variable (`RW`).
|
||||
Rw,
|
||||
/// An enumerated type, which supports a few specific values (`ENUM`).
|
||||
Enum,
|
||||
/// A string with a maximum size (`STRING:n`).
|
||||
String(usize),
|
||||
/// A numeric type, either integer or float, comprised in the range defined by `LIST RANGE`.
|
||||
Range,
|
||||
/// A simple numeric value, either integer or float.
|
||||
Number,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for VariableType {
|
||||
type Error = crate::ClientError;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
"RW" => Ok(Self::Rw),
|
||||
"ENUM" => Ok(Self::Enum),
|
||||
"RANGE" => Ok(Self::Range),
|
||||
"NUMBER" => Ok(Self::Number),
|
||||
other => {
|
||||
if other.starts_with("STRING:") {
|
||||
let size = other
|
||||
.splitn(2, ':')
|
||||
.nth(1)
|
||||
.map(|s| s.parse().ok())
|
||||
.flatten()
|
||||
.ok_or_else(|| {
|
||||
crate::ClientError::Nut(crate::NutError::Generic(
|
||||
"Invalid STRING definition".into(),
|
||||
))
|
||||
})?;
|
||||
Ok(Self::String(size))
|
||||
} else {
|
||||
Err(crate::ClientError::Nut(crate::NutError::Generic(format!(
|
||||
"Unrecognized variable type: {}",
|
||||
value
|
||||
))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// NUT Variable definition.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct VariableDefinition(String, HashSet<VariableType>);
|
||||
|
||||
impl VariableDefinition {
|
||||
/// The name of this variable.
|
||||
pub fn name(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
|
||||
/// Whether this variable is mutable.
|
||||
pub fn is_mutable(&self) -> bool {
|
||||
self.1.contains(&VariableType::Rw)
|
||||
}
|
||||
|
||||
/// Whether this variable is an enumerated type.
|
||||
pub fn is_enum(&self) -> bool {
|
||||
self.1.contains(&VariableType::Enum)
|
||||
}
|
||||
|
||||
/// Whether this variable is a String type
|
||||
pub fn is_string(&self) -> bool {
|
||||
self.1.iter().any(|t| matches!(t, VariableType::String(_)))
|
||||
}
|
||||
|
||||
/// Whether this variable is a numeric type,
|
||||
/// either integer or float, comprised in a range
|
||||
pub fn is_range(&self) -> bool {
|
||||
self.1.contains(&VariableType::Range)
|
||||
}
|
||||
|
||||
/// Whether this variable is a numeric type, either integer or float.
|
||||
pub fn is_number(&self) -> bool {
|
||||
self.1.contains(&VariableType::Number)
|
||||
}
|
||||
|
||||
/// Returns the max string length, if applicable.
|
||||
pub fn get_string_length(&self) -> Option<usize> {
|
||||
self.1.iter().find_map(|t| match t {
|
||||
VariableType::String(n) => Some(*n),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ToString> TryFrom<(A, Vec<&str>)> for VariableDefinition {
|
||||
type Error = crate::ClientError;
|
||||
|
||||
fn try_from(value: (A, Vec<&str>)) -> Result<Self, Self::Error> {
|
||||
Ok(VariableDefinition(
|
||||
value.0.to_string(),
|
||||
value
|
||||
.1
|
||||
.iter()
|
||||
.map(|s| VariableType::try_from(*s))
|
||||
.collect::<crate::Result<HashSet<VariableType>>>()?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_variable_definition() {
|
||||
assert_eq!(
|
||||
VariableDefinition::try_from(("var0", vec![])).unwrap(),
|
||||
VariableDefinition("var0".into(), HashSet::new())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
VariableDefinition::try_from(("var1", vec!["RW"])).unwrap(),
|
||||
VariableDefinition(
|
||||
"var1".into(),
|
||||
HashSet::from_iter(vec![VariableType::Rw].into_iter())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
VariableDefinition::try_from(("var1", vec!["RW", "STRING:123"])).unwrap(),
|
||||
VariableDefinition(
|
||||
"var1".into(),
|
||||
HashSet::from_iter(vec![VariableType::Rw, VariableType::String(123)].into_iter())
|
||||
)
|
||||
);
|
||||
|
||||
assert!(
|
||||
VariableDefinition::try_from(("var1", vec!["RW", "STRING:123"]))
|
||||
.unwrap()
|
||||
.is_mutable()
|
||||
);
|
||||
assert!(
|
||||
VariableDefinition::try_from(("var1", vec!["RW", "STRING:123"]))
|
||||
.unwrap()
|
||||
.is_string()
|
||||
);
|
||||
assert!(
|
||||
!VariableDefinition::try_from(("var1", vec!["RW", "STRING:123"]))
|
||||
.unwrap()
|
||||
.is_enum()
|
||||
);
|
||||
assert!(
|
||||
!VariableDefinition::try_from(("var1", vec!["RW", "STRING:123"]))
|
||||
.unwrap()
|
||||
.is_number()
|
||||
);
|
||||
assert!(
|
||||
!VariableDefinition::try_from(("var1", vec!["RW", "STRING:123"]))
|
||||
.unwrap()
|
||||
.is_range()
|
||||
);
|
||||
assert_eq!(
|
||||
VariableDefinition::try_from(("var1", vec!["RW", "STRING:123"]))
|
||||
.unwrap()
|
||||
.get_string_length(),
|
||||
Some(123)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue