Start work on generic protocol impl for #28 (#29)

This commit is contained in:
Aram Peres 2021-08-04 02:11:44 -04:00 committed by GitHub
parent ff77c1bdbd
commit 1e3481e18d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 1450 additions and 0 deletions

View file

@ -12,6 +12,12 @@ pub use var::*;
/// Blocking client implementation for NUT.
pub mod blocking;
/// NUT protocol implementation (v1.2).
///
/// Reference: <https://networkupstools.org/docs/developer-guide.chunked/ar01s09.html>
#[allow(dead_code)]
#[macro_use]
pub mod proto;
/// Async client implementation for NUT, using Tokio.
#[cfg(feature = "async")]
pub mod tokio;

666
rups/src/proto/client.rs Normal file
View file

@ -0,0 +1,666 @@
use crate::proto::impl_sentences;
impl_sentences! {
/// A generic successful response with no additional data.
GenericOk (
{
0: Ok,
1: EOL,
},
{}
),
/// Forced shut down (FSD) successful.
FsdOk (
{
0: Ok,
1: FsdSet,
2: EOL,
},
{}
),
/// Server acknowledges TLS upgrade.
StartTLSOk (
{
0: Ok,
1: StartTLS,
2: EOL,
},
{}
),
/// Server confirms logout.
LogoutOk (
{
0: Ok,
1: Goodbye,
},
{}
),
/// Server returns an error.
RespondErr (
{
0: Err,
1: Arg,
},
{
/// The error code.
1: message,
},
{
/// Extra information about the error.
2...: extras
}
),
/// Server responds with the number of prior logins to the given `ups_name` device.
RespondNumLogins (
{
0: NumLogins,
1: Arg,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The number of logins to the UPS device.
2: num_logins,
}
),
/// Server responds with the description of the UPS device.
RespondUpsDesc (
{
0: UpsDesc,
1: Arg,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The description of the UPS device.
2: description,
}
),
/// Server responds with the value of the given `var_name` variable for the UPS device.
RespondVar (
{
0: Var,
1: Arg,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the variable.
2: var_name,
/// The current value of the variable.
3: value,
}
),
/// Server responds with the type of the given `var_name` variable for the UPS device.
RespondType (
{
0: Type,
1: Arg,
2: Arg,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the variable.
2: var_name,
},
{
/// The variable definition (RW, ENUN, STRING...)
3...: var_types
}
),
/// Server responds with the description of the given `var_name` variable for the UPS device.
RespondDesc (
{
0: Desc,
1: Arg,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the variable.
2: var_name,
/// The description of the variable.
3: description,
}
),
/// Server responds with the description of the given `cmd_name` command for the UPS device.
RespondCmdDesc (
{
0: CmdDesc,
1: Arg,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the command.
2: cmd_name,
/// The description of the command.
3: description,
}
),
/// Server responds with the name and description of a UPS device.
RespondUps (
{
0: Ups,
1: Arg,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the command.
2: description,
}
),
/// Server responds with the name and description of a mutable variable.
RespondRw (
{
0: Rw,
1: Arg,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the variable.
2: var_name,
/// The current value of the variable.
3: value,
}
),
/// Server responds with the name of a command.
RespondCmd (
{
0: Cmd,
1: Arg,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the command.
2: cmd_name,
}
),
/// Server responds with a possible value of an enumerable variable.
RespondEnum (
{
0: Enum,
1: Arg,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the variable.
2: var_name,
/// A possible value of the variable.
3: enum_value,
}
),
/// Server responds with a possible range of an numeric variable.
RespondRange (
{
0: Range,
1: Arg,
2: Arg,
3: Arg,
4: Arg,
5: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the variable.
2: var_name,
/// The minimum value of the range.
3: min_value,
/// The maximum value of the range.
4: max_value,
}
),
/// Server responds with a client connected to a UPS device.
RespondClient (
{
0: Client,
1: Arg,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The IP address of the client.
2: client_ip,
}
),
/// Server begins returning a list of UPS devices.
BeginListUps (
{
0: Begin,
1: List,
2: Ups,
3: EOL,
},
{}
),
/// Server ends returning a list of UPS devices.
EndListUps (
{
0: End,
1: List,
2: Ups,
3: EOL,
},
{}
),
/// Server begins returning a list of variables for a UPS device.
BeginListVar (
{
0: Begin,
1: List,
2: Var,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
/// Server ends returning a list of variables for a UPS device.
EndListVar (
{
0: End,
1: List,
2: Var,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
/// Server begins returning a list of mutable variables for a UPS device.
BeginListRw (
{
0: Begin,
1: List,
2: Rw,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
/// Server ends returning a list of mutable variables for a UPS device.
EndListRw (
{
0: End,
1: List,
2: Rw,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
/// Server begins returning a list of commands for a UPS device.
BeginListCmd (
{
0: Begin,
1: List,
2: Cmd,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
/// Server ends returning a list of commands for a UPS device.
EndListCmd (
{
0: End,
1: List,
2: Cmd,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
/// Server begins returning a list of possible values for an enumerable variable.
BeginListEnum (
{
0: Begin,
1: List,
2: Enum,
3: Arg,
4: Arg,
5: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
/// The name of the variable.
4: var_name,
}
),
/// Server ends returning a list of possible values for an enumerable variable.
EndListEnum (
{
0: End,
1: List,
2: Enum,
3: Arg,
4: Arg,
5: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
/// The name of the variable.
4: var_name,
}
),
/// Server begins returning a list of possible ranges for an enumerable variable.
BeginListRange (
{
0: Begin,
1: List,
2: Range,
3: Arg,
4: Arg,
5: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
/// The name of the variable.
4: var_name,
}
),
/// Server ends returning a list of possible ranges for an enumerable variable.
EndListRange (
{
0: End,
1: List,
2: Range,
3: Arg,
4: Arg,
5: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
/// The name of the variable.
4: var_name,
}
),
/// Server begins returning a list of clients for a UPS device.
BeginListClient (
{
0: Begin,
1: List,
2: Client,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
/// Server ends returning a list of clients for a UPS device.
EndListClient (
{
0: End,
1: List,
2: Client,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
3: ups_name,
}
),
}
#[cfg(test)]
mod tests {
use crate::proto::test_encode_decode;
use super::Sentences;
#[test]
fn test_encode_decode() {
test_encode_decode!(
["OK"] <=>
Sentences::GenericOk {}
);
test_encode_decode!(
["OK", "FSD-SET"] <=>
Sentences::FsdOk {}
);
test_encode_decode!(
["OK", "STARTTLS"] <=>
Sentences::StartTLSOk {}
);
test_encode_decode!(
["OK", "Goodbye"] <=>
Sentences::LogoutOk {}
);
test_encode_decode!(
["ERR", "ACCESS-DENIED"] <=>
Sentences::RespondErr {
message: "ACCESS-DENIED".into(),
extras: vec![],
}
);
test_encode_decode!(
["ERR", "ACCESS-DENIED", "extra1", "extra2"] <=>
Sentences::RespondErr {
message: "ACCESS-DENIED".into(),
extras: vec!["extra1".into(), "extra2".into()],
}
);
test_encode_decode!(
["NUMLOGINS", "nutdev", "42"] <=>
Sentences::RespondNumLogins {
ups_name: "nutdev".into(),
num_logins: "42".into(),
}
);
test_encode_decode!(
["UPSDESC", "nutdev", "Development box"] <=>
Sentences::RespondUpsDesc {
ups_name: "nutdev".into(),
description: "Development box".into(),
}
);
test_encode_decode!(
["VAR", "nutdev", "ups.status", "OL"] <=>
Sentences::RespondVar {
ups_name: "nutdev".into(),
var_name: "ups.status".into(),
value: "OL".into(),
}
);
test_encode_decode!(
["TYPE", "nutdev", "input.transfer.low", "ENUM", "RW"] <=>
Sentences::RespondType {
ups_name: "nutdev".into(),
var_name: "input.transfer.low".into(),
var_types: vec!["ENUM".into(), "RW".into()],
}
);
test_encode_decode!(
["DESC", "nutdev", "ups.status", "UPS status"] <=>
Sentences::RespondDesc {
ups_name: "nutdev".into(),
var_name: "ups.status".into(),
description: "UPS status".into(),
}
);
test_encode_decode!(
["CMDDESC", "nutdev", "load.on", "Turn on the load immediately"] <=>
Sentences::RespondCmdDesc {
ups_name: "nutdev".into(),
cmd_name: "load.on".into(),
description: "Turn on the load immediately".into(),
}
);
test_encode_decode!(
["UPS", "nutdev", "Development box"] <=>
Sentences::RespondUps {
ups_name: "nutdev".into(),
description: "Development box".into(),
}
);
test_encode_decode!(
["RW", "nutdev", "ups.mfr", "APC"] <=>
Sentences::RespondRw {
ups_name: "nutdev".into(),
var_name: "ups.mfr".into(),
value: "APC".into(),
}
);
test_encode_decode!(
["CMD", "nutdev", "do.something"] <=>
Sentences::RespondCmd {
ups_name: "nutdev".into(),
cmd_name: "do.something".into(),
}
);
test_encode_decode!(
["ENUM", "nutdev", "input.transfer.low", "103"] <=>
Sentences::RespondEnum {
ups_name: "nutdev".into(),
var_name: "input.transfer.low".into(),
enum_value: "103".into(),
}
);
test_encode_decode!(
["RANGE", "nutdev", "input.transfer.low", "90", "100"] <=>
Sentences::RespondRange {
ups_name: "nutdev".into(),
var_name: "input.transfer.low".into(),
min_value: "90".into(),
max_value: "100".into(),
}
);
test_encode_decode!(
["CLIENT", "nutdev", "127.0.0.1"] <=>
Sentences::RespondClient {
ups_name: "nutdev".into(),
client_ip: "127.0.0.1".into(),
}
);
test_encode_decode!(
["BEGIN", "LIST", "VAR", "nutdev"] <=>
Sentences::BeginListVar {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["END", "LIST", "VAR", "nutdev"] <=>
Sentences::EndListVar {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["BEGIN", "LIST", "RW", "nutdev"] <=>
Sentences::BeginListRw {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["END", "LIST", "RW", "nutdev"] <=>
Sentences::EndListRw {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["BEGIN", "LIST", "CMD", "nutdev"] <=>
Sentences::BeginListCmd {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["END", "LIST", "CMD", "nutdev"] <=>
Sentences::EndListCmd {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["BEGIN", "LIST", "ENUM", "nutdev", "test.var"] <=>
Sentences::BeginListEnum {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["END", "LIST", "ENUM", "nutdev", "test.var"] <=>
Sentences::EndListEnum {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["BEGIN", "LIST", "RANGE", "nutdev", "test.var"] <=>
Sentences::BeginListRange {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["END", "LIST", "RANGE", "nutdev", "test.var"] <=>
Sentences::EndListRange {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["BEGIN", "LIST", "CLIENT", "nutdev"] <=>
Sentences::BeginListClient {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["END", "LIST", "CLIENT", "nutdev"] <=>
Sentences::EndListClient {
ups_name: "nutdev".into(),
}
);
}
}

306
rups/src/proto/mod.rs Normal file
View file

@ -0,0 +1,306 @@
/// Client-bound protocol implementation.
///
/// "Client-bound" implies commands RECEIVED and DECODED by the client. The server implementation
/// must use the same messages to ENCODE and SEND.
pub mod client;
/// Server-bound protocol implementation.
///
/// "Server-bound" implies commands RECEIVED and DECODED by the server. The client implementation
/// must use the same messages to ENCODE and SEND.
pub mod server;
/// Macro that implements the list of "words" in the NUT network protocol.
macro_rules! impl_words {
(
$(
$(#[$attr:meta])+
$name:ident($word:tt),
)*
) => {
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum Word {
/// A string argument.
Arg,
/// End-of-line.
EOL,
$(
/// Protocol word.
$(#[$attr])*
#[allow(dead_code)]
$name,
)*
}
impl Word {
/// Matches a raw string into the corresponding word.
/// Passing `None` will always return `EOL`. Passing an unrecognized
/// string returns `None`.
pub(crate) fn decode(raw: Option<&str>) -> Option<Self> {
if let Some(raw) = raw {
match raw {
$($word => Some(Self::$name),)*
_ => None
}
} else {
Some(Self::EOL)
}
}
/// Decodes a sequence of words.
/// Unrecognized words will be `None`
/// Returns a `Vec` of the same length as the given slice.
pub(crate) fn decode_words<T: AsRef<str>>(raw: &[T]) -> Vec<Option<Self>> {
let mut words = Vec::new();
for r in raw.iter() {
words.push(Self::decode(Some(r.as_ref())));
}
words.push(Some(Self::EOL));
words
}
/// Encodes a `Word` into a string.
/// This function cannot encode `Arg` or `EOL` (either returns `None`).
pub(crate) fn encode(&self) -> Option<&str> {
match self {
Self::Arg | Self::EOL => None,
$(Self::$name => Some($word),)*
}
}
/// Whether the `Word` matches another.
pub(crate) fn matches(&self, other: Option<&Option<Self>>) -> bool {
if let Some(other) = other {
if self == &Word::Arg {
true
} else if let Some(other) = other {
self == other
} else {
self == &Word::EOL
}
} else {
false
}
}
/// Whether the `Word` matches all words in the vec, starting at the given index.
pub(crate) fn matches_until_end(&self, start: usize, others: &[Option<Self>]) -> bool {
for i in start..others.len() {
if i == others.len() {
return others[i] == Some(Self::EOL);
}
if !self.matches(others.get(i)) {
return false;
}
}
true
}
}
};
}
/// Implements the list of sentences, which are combinations
/// of words that form commands (serverbound) and responses (clientbound).
macro_rules! impl_sentences {
(
$(
$(#[$attr:meta])+
$name:ident(
{
$($wordidx:tt: $word:ident,)*
},
{
$(
$(#[$argattr:meta])+
$argidx:tt: $arg:ident,
)*
}
$(
,{
$(#[$varargattr:meta])+
$varargidx:tt...: $vararg:ident
}
)?
),
)*
) => {
/// Protocol sentences.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Sentences {
$(
$(#[$attr])*
$name {
$(
$(#[$argattr])*
$arg: String,
)*
$(
$(#[$varargattr])*
$vararg: Vec<String>,
)*
},
)*
}
impl Sentences {
/// Decodes a sentence. Returns `None` if the pattern cannot be recognized.
pub(crate) fn decode(raw: Vec<String>) -> Option<Sentences> {
use super::{Word::*, *};
use Sentences::*;
let words = Word::decode_words(raw.as_slice());
$(
if true
$(&& $word.matches(words.get($wordidx)))*
$(&& Arg.matches_until_end($varargidx, &words))*
{
return Some($name {
$($arg: raw[$argidx].to_owned(),)*
$($vararg: raw[$varargidx..].to_owned(),)*
})
}
)*
None
}
/// Encodes the sentence.
pub(crate) fn encode(&self) -> Vec<&str> {
use super::Word::*;
match self {
$(
Self::$name {
$($arg,)*
$($vararg,)*
} => {
#[allow(unused_mut)]
let mut words = vec![
$(
$word.encode(),
)*
];
$(
words[$argidx] = Some($arg);
)*
$(
for vararg in $vararg {
words.push(Some(vararg));
}
)*
words
.into_iter()
.flatten()
.collect()
}
)*
}
}
}
};
}
/// Macro that asserts the encoding and decoding of a valid sentence.
///
/// The two arguments, separated by `<=>`, are:
/// 1. the encoded sentence, e.g. `["GET", "VAR", "nutdev", "test.var"]`
/// 2. the decoded sentence
///
/// ```
/// test_encode_decode!(
/// ["GET", "VAR", "nutdev", "test.var"] <=>
/// Sentences::QueryVar {
/// ups_name: "nutdev".into(),
/// var_name: "test.var".into(),
/// }
/// );
/// ```
#[allow(unused_macros)]
macro_rules! test_encode_decode {
([$($word:expr$(,)?)+] <=> $expected:expr) => {
assert_eq!(
Sentences::decode(vec![
$(String::from($word),)*
]),
Some($expected)
);
assert_eq!(
vec![
$(String::from($word),)*
],
$expected.encode()
);
};
}
impl_words! {
/// Begins a `LIST`.
Begin("BEGIN"),
/// Describes a client connected to a UPS.
Client("CLIENT"),
/// Represents an executable command.
Cmd("CMD"),
/// Describes a command (`CMD`).
CmdDesc("CMDDESC"),
/// Describes a variable (`VAR` or `RW`).
Desc("DESC"),
/// Ends a block of sentences.
End("END"),
/// An enumerable type.
Enum("ENUM"),
/// An error response.
Err("ERR"),
/// Executes a forced shut down (FSD).
Fsd("FSD"),
/// Server confirms forced shut down (FSD).
FsdSet("FSD-SET"),
/// Serverbound query.
Get("GET"),
/// Server confirms logout (this is lower-case on purpose).
Goodbye("Goodbye"),
/// Client requesting a list of commands supported by the server.
Help("HELP"),
/// Executes an instant command.
InstCmd("INSTCMD"),
/// Queries or describes a list.
List("LIST"),
/// Client requests login to a UPS device.
Login("LOGIN"),
/// Client logs out.
Logout("LOGOUT"),
/// Client verifying it has master-level access to the UPS device.
Master("MASTER"),
/// Client requests the network version.
NetworkVersion("NETVER"),
/// Represents the amount of logins to a UPS device.
NumLogins("NUMLOGINS"),
/// Clientbound response for a good outcome.
Ok("OK"),
/// Client setting password.
Password("PASSWORD"),
/// Represents a range of numerical values.
Range("RANGE"),
/// Represents a mutable variable.
Rw("RW"),
/// Client requests to set the value of a mutable variable.
Set("SET"),
/// Client requests the connection be upgraded to TLS.
StartTLS("STARTTLS"),
/// Represents the type of a variable.
Type("TYPE"),
/// Represents a UPS device.
Ups("UPS"),
/// Represents the description of a UPS device.
UpsDesc("UPSDESC"),
/// Client setting username.
Username("USERNAME"),
/// Represents a variable.
Var("VAR"),
/// Client requests the server version.
Version("VERSION"),
}
pub(crate) use impl_sentences;
#[cfg(test)]
pub(crate) use test_encode_decode;

472
rups/src/proto/server.rs Normal file
View file

@ -0,0 +1,472 @@
use crate::proto::impl_sentences;
impl_sentences! {
/// Client requests the number of prior logins to the given `ups_name` device.
QueryNumLogins (
{
0: Get,
1: NumLogins,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
}
),
/// Client requests the description of the given `ups_name` device.
QueryUpsDesc (
{
0: Get,
1: UpsDesc,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
}
),
/// Client requests the value of the given `var_name` variable in the given `ups_name` device.
QueryVar (
{
0: Get,
1: Var,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
/// The name of the variable.
3: var_name,
}
),
/// Client requests the type of the given `var_name` variable in the given `ups_name` device.
QueryType (
{
0: Get,
1: Type,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
/// The name of the variable.
3: var_name,
}
),
/// Client requests the description of the given `var_name` variable in the given `ups_name` device.
QueryDesc (
{
0: Get,
1: Desc,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
/// The name of the variable.
3: var_name,
}
),
/// Client requests the description of the given `cmd_name` command in the given `ups_name` device.
QueryCmdDesc (
{
0: Get,
1: CmdDesc,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
/// The name of the command.
3: cmd_name,
}
),
/// Client requests the list of variables for the given `ups_name` device.
QueryListVar (
{
0: List,
1: Var,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
}
),
/// Client requests the list of mutable variables for the given `ups_name` device.
QueryListRw (
{
0: List,
1: Rw,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
}
),
/// Client requests the list of commands for the given `ups_name` device.
QueryListCmd (
{
0: List,
1: Cmd,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
}
),
/// Client requests the list of possible values of the enumerable variable `var_name`
/// for the given `ups_name` device.
QueryListEnum (
{
0: List,
1: Enum,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
/// The name of the variable.
3: var_name,
}
),
/// Client requests the list of possible ranges of the numerical variable `var_name`
/// for the given `ups_name` device.
QueryListRange (
{
0: List,
1: Range,
2: Arg,
3: Arg,
4: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
/// The name of the variable.
3: var_name,
}
),
/// Client requests the list of clients connected to the given `ups_name` device.
QueryListClient (
{
0: List,
1: Client,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
}
),
/// Client requests to set the value `value` of the `var_name` variable on the `ups_name` device.
ExecSetVar (
{
0: Set,
1: Var,
2: Arg,
3: Arg,
4: Arg,
5: EOL,
},
{
/// The name of the UPS device.
2: ups_name,
/// The name of the variable.
3: var_name,
/// The new value of the variable.
4: value,
}
),
/// Client requests the execution of an instant command `cmd_name` on the `ups_name` device.
ExecInstCmd (
{
0: InstCmd,
1: Arg,
2: Arg,
3: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
/// The name of the command.
2: cmd_name,
}
),
/// Client logs-out of the current UPS device.
ExecLogout (
{
0: Logout,
1: EOL,
},
{}
),
/// Client logs-into the given `ups_name` device.
ExecLogin (
{
0: Login,
1: Arg,
2: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
}
),
/// Client asserts master-level access to the `ups_name` device.
ExecMaster (
{
0: Master,
1: Arg,
2: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
}
),
/// Client requests the forced shut-down of the `ups_name` device.
ExecForcedShutDown (
{
0: Fsd,
1: Arg,
2: EOL,
},
{
/// The name of the UPS device.
1: ups_name,
}
),
/// Client sets the password on the connection.
SetPassword (
{
0: Password,
1: Arg,
2: EOL,
},
{
/// The password to set.
1: password,
}
),
/// Client sets the username on the connection.
SetUsername (
{
0: Username,
1: Arg,
2: EOL,
},
{
/// The username to set.
1: username,
}
),
/// Client requests the connection be upgraded to TLS.
ExecStartTLS (
{
0: StartTLS,
1: EOL,
},
{}
),
/// Client requests the list of commands supported by the server.
QueryHelp (
{
0: Help,
1: EOL,
},
{}
),
/// Client requests the server version.
QueryVersion (
{
0: Version,
1: EOL,
},
{}
),
/// Client requests the network version.
QueryNetworkVersion (
{
0: NetworkVersion,
1: EOL,
},
{}
),
}
#[cfg(test)]
mod tests {
use super::Sentences;
use crate::proto::test_encode_decode;
#[test]
fn test_encode_decode() {
test_encode_decode!(
["GET", "NUMLOGINS", "nutdev"] <=>
Sentences::QueryNumLogins {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["GET", "NUMLOGINS", "nutdev"] <=>
Sentences::QueryNumLogins {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["GET", "UPSDESC", "nutdev"] <=>
Sentences::QueryUpsDesc {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["GET", "VAR", "nutdev", "test.var"] <=>
Sentences::QueryVar {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["GET", "TYPE", "nutdev", "test.var"] <=>
Sentences::QueryType {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["GET", "DESC", "nutdev", "test.var"] <=>
Sentences::QueryDesc {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["GET", "CMDDESC", "nutdev", "test.cmd"] <=>
Sentences::QueryCmdDesc {
ups_name: "nutdev".into(),
cmd_name: "test.cmd".into(),
}
);
test_encode_decode!(
["LIST", "VAR", "nutdev"] <=>
Sentences::QueryListVar {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["LIST", "RW", "nutdev"] <=>
Sentences::QueryListRw {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["LIST", "CMD", "nutdev"] <=>
Sentences::QueryListCmd {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["LIST", "ENUM", "nutdev", "test.var"] <=>
Sentences::QueryListEnum {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["LIST", "RANGE", "nutdev", "test.var"] <=>
Sentences::QueryListRange {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
}
);
test_encode_decode!(
["LIST", "CLIENT", "nutdev"] <=>
Sentences::QueryListClient {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["SET", "VAR", "nutdev", "test.var", "something"] <=>
Sentences::ExecSetVar {
ups_name: "nutdev".into(),
var_name: "test.var".into(),
value: "something".into(),
}
);
test_encode_decode!(
["INSTCMD", "nutdev", "test.cmd"] <=>
Sentences::ExecInstCmd {
ups_name: "nutdev".into(),
cmd_name: "test.cmd".into(),
}
);
test_encode_decode!(
["LOGOUT"] <=>
Sentences::ExecLogout {}
);
test_encode_decode!(
["LOGIN", "nutdev"] <=>
Sentences::ExecLogin {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["MASTER", "nutdev"] <=>
Sentences::ExecMaster {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["FSD", "nutdev"] <=>
Sentences::ExecForcedShutDown {
ups_name: "nutdev".into(),
}
);
test_encode_decode!(
["PASSWORD", "topsecret"] <=>
Sentences::SetPassword {
password: "topsecret".into(),
}
);
test_encode_decode!(
["USERNAME", "john"] <=>
Sentences::SetUsername {
username: "john".into(),
}
);
test_encode_decode!(
["STARTTLS"] <=>
Sentences::ExecStartTLS {}
);
test_encode_decode!(
["HELP"] <=>
Sentences::QueryHelp {}
);
test_encode_decode!(
["VERSION"] <=>
Sentences::QueryVersion {}
);
test_encode_decode!(
["NETVER"] <=>
Sentences::QueryNetworkVersion {}
);
}
}