mirror of
https://github.com/arampoire/nut-rs.git
synced 2025-12-01 00:30:23 -05:00
rupsc: parsing and basic usage (minus clients)
This commit is contained in:
parent
80ceb6f1df
commit
f3d9195bc4
8 changed files with 384 additions and 22 deletions
|
|
@ -38,6 +38,16 @@ impl Connection {
|
||||||
.collect()),
|
.collect()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Queries one variable for a UPS device.
|
||||||
|
pub fn get_var(&mut self, ups_name: &str, variable: &str) -> crate::Result<Variable> {
|
||||||
|
match self {
|
||||||
|
Self::Tcp(conn) => {
|
||||||
|
let var = conn.get_var(ups_name, variable)?;
|
||||||
|
Ok(Variable::parse(var.0.as_str(), var.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A blocking TCP NUT client connection.
|
/// A blocking TCP NUT client connection.
|
||||||
|
|
@ -95,6 +105,15 @@ impl TcpConnection {
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_var(&mut self, ups_name: &str, variable: &str) -> crate::Result<(String, String)> {
|
||||||
|
let query = &["VAR", ups_name, variable];
|
||||||
|
Self::write_cmd(&mut self.tcp_stream, Command::Get(query))?;
|
||||||
|
|
||||||
|
let resp = Self::read_response(&mut self.tcp_stream)?;
|
||||||
|
let (name, value) = resp.expect_var()?;
|
||||||
|
Ok((name.into(), value.into()))
|
||||||
|
}
|
||||||
|
|
||||||
fn write_cmd(stream: &mut TcpStream, line: Command) -> crate::Result<()> {
|
fn write_cmd(stream: &mut TcpStream, line: Command) -> crate::Result<()> {
|
||||||
let line = format!("{}\n", line);
|
let line = format!("{}\n", line);
|
||||||
stream.write_all(line.as_bytes())?;
|
stream.write_all(line.as_bytes())?;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use crate::NutError;
|
use crate::{ClientError, NutError};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Command<'a> {
|
pub enum Command<'a> {
|
||||||
|
Get(&'a [&'a str]),
|
||||||
/// Passes the login username.
|
/// Passes the login username.
|
||||||
SetUsername(&'a str),
|
SetUsername(&'a str),
|
||||||
/// Passes the login password.
|
/// Passes the login password.
|
||||||
|
|
@ -16,6 +17,7 @@ impl<'a> Command<'a> {
|
||||||
/// The network identifier of the command.
|
/// The network identifier of the command.
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Get(_) => "GET",
|
||||||
Self::SetUsername(_) => "USERNAME",
|
Self::SetUsername(_) => "USERNAME",
|
||||||
Self::SetPassword(_) => "PASSWORD",
|
Self::SetPassword(_) => "PASSWORD",
|
||||||
Self::List(_) => "LIST",
|
Self::List(_) => "LIST",
|
||||||
|
|
@ -25,6 +27,7 @@ impl<'a> Command<'a> {
|
||||||
/// The arguments of the command to serialize.
|
/// The arguments of the command to serialize.
|
||||||
pub fn args(&self) -> Vec<&str> {
|
pub fn args(&self) -> Vec<&str> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Get(cmd) => cmd.to_vec(),
|
||||||
Self::SetUsername(username) => vec![username],
|
Self::SetUsername(username) => vec![username],
|
||||||
Self::SetPassword(password) => vec![password],
|
Self::SetPassword(password) => vec![password],
|
||||||
Self::List(query) => query.to_vec(),
|
Self::List(query) => query.to_vec(),
|
||||||
|
|
@ -48,6 +51,8 @@ pub enum Response {
|
||||||
BeginList(String),
|
BeginList(String),
|
||||||
/// Marks the end of a list response.
|
/// Marks the end of a list response.
|
||||||
EndList(String),
|
EndList(String),
|
||||||
|
/// A variable response.
|
||||||
|
Var(String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
|
|
@ -109,6 +114,23 @@ impl Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"VAR" => {
|
||||||
|
let var_name = if args.is_empty() {
|
||||||
|
Err(ClientError::from(NutError::Generic(
|
||||||
|
"Unspecified VAR name in response".into(),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(args.remove(0))
|
||||||
|
}?;
|
||||||
|
let var_value = if args.is_empty() {
|
||||||
|
Err(ClientError::from(NutError::Generic(
|
||||||
|
"Unspecified VAR value in response".into(),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(args.remove(0))
|
||||||
|
}?;
|
||||||
|
Ok(Response::Var(var_name, var_value))
|
||||||
|
}
|
||||||
_ => Err(NutError::UnknownResponseType(cmd_name).into()),
|
_ => Err(NutError::UnknownResponseType(cmd_name).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,4 +167,12 @@ impl Response {
|
||||||
Err(NutError::UnexpectedResponse.into())
|
Err(NutError::UnexpectedResponse.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expect_var(&self) -> crate::Result<(&str, &str)> {
|
||||||
|
if let Self::Var(name, value) = &self {
|
||||||
|
Ok((name, value))
|
||||||
|
} else {
|
||||||
|
Err(NutError::UnexpectedResponse.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@ impl Default for Host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SocketAddr> for Host {
|
||||||
|
fn from(addr: SocketAddr) -> Self {
|
||||||
|
Self::Tcp(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An authentication mechanism.
|
/// An authentication mechanism.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Auth {
|
pub struct Auth {
|
||||||
|
|
|
||||||
|
|
@ -79,28 +79,46 @@ impl Variable {
|
||||||
_ => Self::Other((name.into(), value)),
|
_ => Self::Other((name.into(), value)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the NUT name of the variable.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
use self::key::*;
|
||||||
|
match self {
|
||||||
|
Self::DeviceModel(_) => DEVICE_MODEL,
|
||||||
|
Self::DeviceManufacturer(_) => DEVICE_MANUFACTURER,
|
||||||
|
Self::DeviceSerial(_) => DEVICE_SERIAL,
|
||||||
|
Self::DeviceType(_) => DEVICE_TYPE,
|
||||||
|
Self::DeviceDescription(_) => DEVICE_DESCRIPTION,
|
||||||
|
Self::DeviceContact(_) => DEVICE_CONTACT,
|
||||||
|
Self::DeviceLocation(_) => DEVICE_LOCATION,
|
||||||
|
Self::DevicePart(_) => DEVICE_PART,
|
||||||
|
Self::DeviceMacAddress(_) => DEVICE_MAC_ADDRESS,
|
||||||
|
Self::DeviceUptime(_) => DEVICE_UPTIME,
|
||||||
|
Self::Other((name, _)) => name.as_str(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of the NUT variable.
|
||||||
|
pub fn value(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::DeviceModel(value) => value.clone(),
|
||||||
|
Self::DeviceManufacturer(value) => value.clone(),
|
||||||
|
Self::DeviceSerial(value) => value.clone(),
|
||||||
|
Self::DeviceType(value) => value.to_string(),
|
||||||
|
Self::DeviceDescription(value) => value.clone(),
|
||||||
|
Self::DeviceContact(value) => value.clone(),
|
||||||
|
Self::DeviceLocation(value) => value.clone(),
|
||||||
|
Self::DevicePart(value) => value.clone(),
|
||||||
|
Self::DeviceMacAddress(value) => value.clone(),
|
||||||
|
Self::DeviceUptime(value) => value.as_secs().to_string(),
|
||||||
|
Self::Other((_, value)) => value.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Variable {
|
impl fmt::Display for Variable {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use self::key::*;
|
write!(f, "{}: {}", self.name(), self.value())
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::DeviceModel(value) => write!(f, "{} = {}", DEVICE_MODEL, value),
|
|
||||||
Self::DeviceManufacturer(value) => write!(f, "{} = {}", DEVICE_MANUFACTURER, value),
|
|
||||||
Self::DeviceSerial(value) => write!(f, "{} = {}", DEVICE_SERIAL, value),
|
|
||||||
Self::DeviceType(value) => write!(f, "{} = {}", DEVICE_TYPE, value),
|
|
||||||
Self::DeviceDescription(value) => write!(f, "{} = {}", DEVICE_DESCRIPTION, value),
|
|
||||||
Self::DeviceContact(value) => write!(f, "{} = {}", DEVICE_CONTACT, value),
|
|
||||||
Self::DeviceLocation(value) => write!(f, "{} = {}", DEVICE_LOCATION, value),
|
|
||||||
Self::DevicePart(value) => write!(f, "{} = {}", DEVICE_PART, value),
|
|
||||||
Self::DeviceMacAddress(value) => write!(f, "{} = {}", DEVICE_MAC_ADDRESS, value),
|
|
||||||
Self::DeviceUptime(value) => {
|
|
||||||
write!(f, "{} = {} seconds", DEVICE_UPTIME, value.as_secs())
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::Other((key, value)) => write!(f, "other({}) = {}", key, value),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,21 @@
|
||||||
[package]
|
[package]
|
||||||
name = "rupsc"
|
name = "rupsc"
|
||||||
version = "0.1.0"
|
version = "0.0.4"
|
||||||
|
authors = ["Aram Peres <aram.peres@wavy.fm>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
description = "A demo program to display UPS variables"
|
||||||
|
categories = ["network-programming"]
|
||||||
|
keywords = ["ups", "nut"]
|
||||||
|
repository = "https://github.com/aramperes/nut-client-rs"
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = "2.33.3"
|
||||||
|
anyhow = "1"
|
||||||
|
|
||||||
|
[dependencies.nut-client]
|
||||||
|
version = "0.0.4"
|
||||||
|
path = "../nut-client"
|
||||||
|
|
|
||||||
50
rupsc/src/cmd.rs
Normal file
50
rupsc/src/cmd.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::parser::UpsdName;
|
||||||
|
use anyhow::Context;
|
||||||
|
use core::convert::TryInto;
|
||||||
|
use nut_client::blocking::Connection;
|
||||||
|
|
||||||
|
/// Lists each UPS on the upsd server, one per line.
|
||||||
|
pub fn list_devices(server: UpsdName, verbose: bool) -> anyhow::Result<()> {
|
||||||
|
let mut conn = connect(server)?;
|
||||||
|
|
||||||
|
for (name, description) in conn.list_ups()? {
|
||||||
|
if verbose {
|
||||||
|
println!("{}: {}", name, description);
|
||||||
|
} else {
|
||||||
|
println!("{}", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_variable(server: UpsdName, variable: &str) -> anyhow::Result<()> {
|
||||||
|
let ups_name = server
|
||||||
|
.upsname
|
||||||
|
.with_context(|| "ups name must be specified: <upsname>[@<hostname>[:<port>]]")?;
|
||||||
|
let mut conn = connect(server)?;
|
||||||
|
|
||||||
|
let variable = conn.get_var(ups_name, variable)?;
|
||||||
|
println!("{}", variable.value());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_variables(server: UpsdName) -> anyhow::Result<()> {
|
||||||
|
let ups_name = server
|
||||||
|
.upsname
|
||||||
|
.with_context(|| "ups name must be specified: <upsname>[@<hostname>[:<port>]]")?;
|
||||||
|
let mut conn = connect(server)?;
|
||||||
|
|
||||||
|
for var in conn.list_vars(ups_name)? {
|
||||||
|
println!("{}", var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect(server: UpsdName) -> anyhow::Result<Connection> {
|
||||||
|
let host = server.try_into()?;
|
||||||
|
let config = nut_client::ConfigBuilder::new().with_host(host).build();
|
||||||
|
Connection::new(config).with_context(|| format!("Failed to connect to upsd: {}", server))
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,77 @@
|
||||||
fn main() {
|
///! # rupsc
|
||||||
println!("Hello, world!");
|
///! A demo program to display UPS variables.
|
||||||
|
///! This a Rust clone of [upsc](https://github.com/networkupstools/nut/blob/master/clients/upsc.c).
|
||||||
|
///!
|
||||||
|
///! P.S.: pronounced "r-oopsie".
|
||||||
|
mod cmd;
|
||||||
|
mod parser;
|
||||||
|
|
||||||
|
use crate::parser::UpsdName;
|
||||||
|
use anyhow::Context;
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use core::convert::TryInto;
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let args = App::new(clap::crate_name!())
|
||||||
|
.version(clap::crate_version!())
|
||||||
|
.author(clap::crate_authors!())
|
||||||
|
.about(clap::crate_description!())
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("list")
|
||||||
|
.short("l")
|
||||||
|
.conflicts_with_all(&["list-full", "clients"])
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Lists each UPS on <hostname>, one per line."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("list-full")
|
||||||
|
.short("L")
|
||||||
|
.conflicts_with_all(&["list", "clients"])
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Lists each UPS followed by its description (from ups.conf)."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("clients")
|
||||||
|
.short("c")
|
||||||
|
.conflicts_with_all(&["list", "list-full"])
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Lists each client connected on <upsname>, one per line."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("upsd-server")
|
||||||
|
.required(false)
|
||||||
|
.value_name("[upsname][@<hostname>[:<port>]]")
|
||||||
|
.help("upsd server (with optional upsname, if applicable)."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("variable")
|
||||||
|
.required(false)
|
||||||
|
.value_name("variable")
|
||||||
|
.help("Optional, display this variable only."),
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let server: parser::UpsdName = args.value_of("upsd-server").map_or_else(
|
||||||
|
|| Ok(UpsdName::default()),
|
||||||
|
|s| s.try_into().with_context(|| "Invalid upsd server name"),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if args.is_present("list") {
|
||||||
|
return cmd::list_devices(server, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.is_present("list-full") {
|
||||||
|
return cmd::list_devices(server, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.is_present("clients") {
|
||||||
|
todo!("listing clients")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: prints one variable (or all of them)
|
||||||
|
if let Some(variable) = args.value_of("variable") {
|
||||||
|
cmd::print_variable(server, variable)
|
||||||
|
} else {
|
||||||
|
cmd::list_variables(server)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
152
rupsc/src/parser.rs
Normal file
152
rupsc/src/parser.rs
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
use anyhow::Context;
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::fmt;
|
||||||
|
use std::net::ToSocketAddrs;
|
||||||
|
|
||||||
|
pub const DEFAULT_HOSTNAME: &str = "localhost";
|
||||||
|
pub const DEFAULT_PORT: u16 = 3493;
|
||||||
|
|
||||||
|
/// Connection information for a upsd server.
|
||||||
|
///
|
||||||
|
/// The upsname is optional depending on context.
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct UpsdName<'a> {
|
||||||
|
pub upsname: Option<&'a str>,
|
||||||
|
pub hostname: &'a str,
|
||||||
|
pub port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Default for UpsdName<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
UpsdName {
|
||||||
|
upsname: None,
|
||||||
|
hostname: DEFAULT_HOSTNAME,
|
||||||
|
port: DEFAULT_PORT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a str> for UpsdName<'a> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'a str) -> anyhow::Result<UpsdName<'a>> {
|
||||||
|
let mut upsname: Option<&str> = None;
|
||||||
|
let mut hostname = DEFAULT_HOSTNAME;
|
||||||
|
let mut port = DEFAULT_PORT;
|
||||||
|
|
||||||
|
if value.contains(':') {
|
||||||
|
let mut split = value.splitn(2, ':');
|
||||||
|
let prefix = split.next().unwrap();
|
||||||
|
port = split
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.parse::<u16>()
|
||||||
|
.with_context(|| "invalid port number")?;
|
||||||
|
if prefix.contains('@') {
|
||||||
|
let mut split = prefix.splitn(2, '@');
|
||||||
|
upsname = Some(split.next().unwrap());
|
||||||
|
hostname = split.next().unwrap();
|
||||||
|
} else {
|
||||||
|
hostname = prefix;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if value.contains('@') {
|
||||||
|
let mut split = value.splitn(2, '@');
|
||||||
|
upsname = Some(split.next().unwrap());
|
||||||
|
hostname = split.next().unwrap();
|
||||||
|
} else {
|
||||||
|
upsname = Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(UpsdName {
|
||||||
|
upsname,
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryInto<nut_client::Host> for UpsdName<'a> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_into(self) -> anyhow::Result<nut_client::Host> {
|
||||||
|
Ok((String::from(self.hostname), self.port)
|
||||||
|
.to_socket_addrs()
|
||||||
|
.with_context(|| "Failed to convert to SocketAddr")?
|
||||||
|
.next()
|
||||||
|
.with_context(|| "Failed to convert to SocketAddr")?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for UpsdName<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if let Some(upsname) = self.upsname {
|
||||||
|
write!(f, "{}@", upsname)?;
|
||||||
|
}
|
||||||
|
write!(f, "{}:{}", self.hostname, self.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use core::convert::TryInto;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_upsdname_parser_full() {
|
||||||
|
let name: UpsdName = "ups@notlocal:1234".try_into().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
name,
|
||||||
|
UpsdName {
|
||||||
|
upsname: Some("ups"),
|
||||||
|
hostname: "notlocal",
|
||||||
|
port: 1234
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(format!("{}", name), "ups@notlocal:1234");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_upsdname_parser_no_name() {
|
||||||
|
let name: UpsdName = "notlocal:5678".try_into().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
name,
|
||||||
|
UpsdName {
|
||||||
|
upsname: None,
|
||||||
|
hostname: "notlocal",
|
||||||
|
port: 5678
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(format!("{}", name), "notlocal:5678");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_upsdname_parser_no_port_no_hostname() {
|
||||||
|
let name: UpsdName = "ups0".try_into().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
name,
|
||||||
|
UpsdName {
|
||||||
|
upsname: Some("ups0"),
|
||||||
|
hostname: DEFAULT_HOSTNAME,
|
||||||
|
port: DEFAULT_PORT
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(format!("{}", name), "ups0@localhost:3493");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_upsdname_parser_no_port() {
|
||||||
|
let name: UpsdName = "ups@notlocal".try_into().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
name,
|
||||||
|
UpsdName {
|
||||||
|
upsname: Some("ups"),
|
||||||
|
hostname: "notlocal",
|
||||||
|
port: DEFAULT_PORT
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(format!("{}", name), "ups@notlocal:3493");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue