1use core::fmt;
2use std::convert::TryFrom;
3
4use crate::{ClientError, NutError, Variable, VariableDefinition, VariableRange};
5
6#[derive(Debug, Clone)]
7pub enum Command<'a> {
8 Get(&'a [&'a str]),
9 SetUsername(&'a str),
11 SetPassword(&'a str),
13 List(&'a [&'a str]),
15 StartTLS,
17 NetworkVersion,
19 Version,
21 Logout,
23}
24
25impl<'a> Command<'a> {
26 pub fn name(&self) -> &'static str {
28 match self {
29 Self::Get(_) => "GET",
30 Self::SetUsername(_) => "USERNAME",
31 Self::SetPassword(_) => "PASSWORD",
32 Self::List(_) => "LIST",
33 Self::StartTLS => "STARTTLS",
34 Self::NetworkVersion => "NETVER",
35 Self::Version => "VER",
36 Self::Logout => "LOGOUT",
37 }
38 }
39
40 pub fn args(&self) -> Vec<&str> {
42 match self {
43 Self::Get(cmd) => cmd.to_vec(),
44 Self::SetUsername(username) => vec![username],
45 Self::SetPassword(password) => vec![password],
46 Self::List(query) => query.to_vec(),
47 _ => Vec::new(),
48 }
49 }
50}
51
52impl<'a> fmt::Display for Command<'a> {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 let mut args = self.args();
55 args.insert(0, self.name());
56 write!(f, "{}", shell_words::join(args))
57 }
58}
59
60#[derive(Debug, Clone)]
61pub enum Response {
62 Ok,
64 BeginList(String),
66 EndList(String),
68 Var(String, String),
72 Ups(String, String),
76 Client(String),
80 Cmd(String),
84 CmdDesc(String),
88 UpsDesc(String),
92 Rw(String, String),
96 Desc(String),
100 NumLogins(i32),
104 Type(String, Vec<String>),
108 Range(VariableRange),
112 Enum(String),
116}
117
118impl Response {
119 pub fn from_args(mut args: Vec<String>) -> crate::Result<Response> {
120 if args.is_empty() {
121 return Err(
122 NutError::Generic("Parsing server response failed: empty line".into()).into(),
123 );
124 }
125 let cmd_name = args.remove(0);
126 match cmd_name.as_str() {
127 "OK" => Ok(Self::Ok),
128 "ERR" => {
129 if args.is_empty() {
130 Err(NutError::Generic("Unspecified server error".into()).into())
131 } else {
132 let err_type = args.remove(0);
133 match err_type.as_str() {
134 "ACCESS-DENIED" => Err(NutError::AccessDenied.into()),
135 "UNKNOWN-UPS" => Err(NutError::UnknownUps.into()),
136 "FEATURE-NOT-CONFIGURED" => Err(NutError::FeatureNotConfigured.into()),
137 _ => Err(NutError::Generic(format!(
138 "Server error: {} {}",
139 err_type,
140 args.join(" ")
141 ))
142 .into()),
143 }
144 }
145 }
146 "BEGIN" => {
147 if args.is_empty() {
148 Err(NutError::Generic("Unspecified BEGIN type".into()).into())
149 } else {
150 let begin_type = args.remove(0);
151 if &begin_type != "LIST" {
152 Err(
153 NutError::Generic(format!("Unexpected BEGIN type: {}", begin_type))
154 .into(),
155 )
156 } else {
157 let args = shell_words::join(args);
158 Ok(Response::BeginList(args))
159 }
160 }
161 }
162 "END" => {
163 if args.is_empty() {
164 Err(NutError::Generic("Unspecified END type".into()).into())
165 } else {
166 let begin_type = args.remove(0);
167 if &begin_type != "LIST" {
168 Err(
169 NutError::Generic(format!("Unexpected END type: {}", begin_type))
170 .into(),
171 )
172 } else {
173 let args = shell_words::join(args);
174 Ok(Response::EndList(args))
175 }
176 }
177 }
178 "VAR" => {
179 let _var_device = if args.is_empty() {
180 Err(ClientError::from(NutError::Generic(
181 "Unspecified VAR device name in response".into(),
182 )))
183 } else {
184 Ok(args.remove(0))
185 }?;
186 let var_name = if args.is_empty() {
187 Err(ClientError::from(NutError::Generic(
188 "Unspecified VAR name in response".into(),
189 )))
190 } else {
191 Ok(args.remove(0))
192 }?;
193 let var_value = if args.is_empty() {
194 Err(ClientError::from(NutError::Generic(
195 "Unspecified VAR value in response".into(),
196 )))
197 } else {
198 Ok(args.remove(0))
199 }?;
200 Ok(Response::Var(var_name, var_value))
201 }
202 "RW" => {
203 let _var_device = if args.is_empty() {
204 Err(ClientError::from(NutError::Generic(
205 "Unspecified RW device name in response".into(),
206 )))
207 } else {
208 Ok(args.remove(0))
209 }?;
210 let var_name = if args.is_empty() {
211 Err(ClientError::from(NutError::Generic(
212 "Unspecified RW name in response".into(),
213 )))
214 } else {
215 Ok(args.remove(0))
216 }?;
217 let var_value = if args.is_empty() {
218 Err(ClientError::from(NutError::Generic(
219 "Unspecified RW value in response".into(),
220 )))
221 } else {
222 Ok(args.remove(0))
223 }?;
224 Ok(Response::Rw(var_name, var_value))
225 }
226 "UPS" => {
227 let name = if args.is_empty() {
228 Err(ClientError::from(NutError::Generic(
229 "Unspecified UPS name in response".into(),
230 )))
231 } else {
232 Ok(args.remove(0))
233 }?;
234 let description = if args.is_empty() {
235 Err(ClientError::from(NutError::Generic(
236 "Unspecified UPS description in response".into(),
237 )))
238 } else {
239 Ok(args.remove(0))
240 }?;
241 Ok(Response::Ups(name, description))
242 }
243 "CLIENT" => {
244 let _device = if args.is_empty() {
245 Err(ClientError::from(NutError::Generic(
246 "Unspecified CLIENT device in response".into(),
247 )))
248 } else {
249 Ok(args.remove(0))
250 }?;
251 let ip_address = if args.is_empty() {
252 Err(ClientError::from(NutError::Generic(
253 "Unspecified CLIENT IP in response".into(),
254 )))
255 } else {
256 Ok(args.remove(0))
257 }?;
258 Ok(Response::Client(ip_address))
259 }
260 "CMD" => {
261 let _device = if args.is_empty() {
262 Err(ClientError::from(NutError::Generic(
263 "Unspecified CMD device in response".into(),
264 )))
265 } else {
266 Ok(args.remove(0))
267 }?;
268 let name = if args.is_empty() {
269 Err(ClientError::from(NutError::Generic(
270 "Unspecified CMD name in response".into(),
271 )))
272 } else {
273 Ok(args.remove(0))
274 }?;
275 Ok(Response::Cmd(name))
276 }
277 "CMDDESC" => {
278 let _device = if args.is_empty() {
279 Err(ClientError::from(NutError::Generic(
280 "Unspecified CMDDESC device in response".into(),
281 )))
282 } else {
283 Ok(args.remove(0))
284 }?;
285 let _name = if args.is_empty() {
286 Err(ClientError::from(NutError::Generic(
287 "Unspecified CMDDESC name in response".into(),
288 )))
289 } else {
290 Ok(args.remove(0))
291 }?;
292 let desc = if args.is_empty() {
293 Err(ClientError::from(NutError::Generic(
294 "Unspecified CMDDESC description in response".into(),
295 )))
296 } else {
297 Ok(args.remove(0))
298 }?;
299 Ok(Response::CmdDesc(desc))
300 }
301 "UPSDESC" => {
302 let _device = if args.is_empty() {
303 Err(ClientError::from(NutError::Generic(
304 "Unspecified UPSDESC device in response".into(),
305 )))
306 } else {
307 Ok(args.remove(0))
308 }?;
309 let desc = if args.is_empty() {
310 Err(ClientError::from(NutError::Generic(
311 "Unspecified UPSDESC description in response".into(),
312 )))
313 } else {
314 Ok(args.remove(0))
315 }?;
316 Ok(Response::UpsDesc(desc))
317 }
318 "DESC" => {
319 let _device = if args.is_empty() {
320 Err(ClientError::from(NutError::Generic(
321 "Unspecified DESC device in response".into(),
322 )))
323 } else {
324 Ok(args.remove(0))
325 }?;
326 let _name = if args.is_empty() {
327 Err(ClientError::from(NutError::Generic(
328 "Unspecified DESC name in response".into(),
329 )))
330 } else {
331 Ok(args.remove(0))
332 }?;
333 let desc = if args.is_empty() {
334 Err(ClientError::from(NutError::Generic(
335 "Unspecified DESC description in response".into(),
336 )))
337 } else {
338 Ok(args.remove(0))
339 }?;
340 Ok(Response::Desc(desc))
341 }
342 "NUMLOGINS" => {
343 let _device = if args.is_empty() {
344 Err(ClientError::from(NutError::Generic(
345 "Unspecified NUMLOGINS device in response".into(),
346 )))
347 } else {
348 Ok(args.remove(0))
349 }?;
350 let num = if args.is_empty() {
351 Err(ClientError::from(NutError::Generic(
352 "Unspecified NUMLOGINS number in response".into(),
353 )))
354 } else {
355 Ok(args.remove(0))
356 }?;
357 let num = num.parse::<i32>().map_err(|_| {
358 ClientError::from(NutError::Generic(
359 "Invalid NUMLOGINS number in response".into(),
360 ))
361 })?;
362 Ok(Response::NumLogins(num))
363 }
364 "TYPE" => {
365 let _device = if args.is_empty() {
366 Err(ClientError::from(NutError::Generic(
367 "Unspecified TYPE device in response".into(),
368 )))
369 } else {
370 Ok(args.remove(0))
371 }?;
372 let name = if args.is_empty() {
373 Err(ClientError::from(NutError::Generic(
374 "Unspecified TYPE name in response".into(),
375 )))
376 } else {
377 Ok(args.remove(0))
378 }?;
379 let types = args;
380 Ok(Response::Type(name, types))
381 }
382 "RANGE" => {
383 let _device = if args.is_empty() {
384 Err(ClientError::from(NutError::Generic(
385 "Unspecified RANGE device in response".into(),
386 )))
387 } else {
388 Ok(args.remove(0))
389 }?;
390 let _name = if args.is_empty() {
391 Err(ClientError::from(NutError::Generic(
392 "Unspecified RANGE name in response".into(),
393 )))
394 } else {
395 Ok(args.remove(0))
396 }?;
397 let min = if args.is_empty() {
398 Err(ClientError::from(NutError::Generic(
399 "Unspecified RANGE min in response".into(),
400 )))
401 } else {
402 Ok(args.remove(0))
403 }?;
404 let max = if args.is_empty() {
405 Err(ClientError::from(NutError::Generic(
406 "Unspecified RANGE max in response".into(),
407 )))
408 } else {
409 Ok(args.remove(0))
410 }?;
411 Ok(Response::Range(VariableRange(min, max)))
412 }
413 "ENUM" => {
414 let _device = if args.is_empty() {
415 Err(ClientError::from(NutError::Generic(
416 "Unspecified ENUM device in response".into(),
417 )))
418 } else {
419 Ok(args.remove(0))
420 }?;
421 let _name = if args.is_empty() {
422 Err(ClientError::from(NutError::Generic(
423 "Unspecified ENUM name in response".into(),
424 )))
425 } else {
426 Ok(args.remove(0))
427 }?;
428 let val = if args.is_empty() {
429 Err(ClientError::from(NutError::Generic(
430 "Unspecified ENUM value in response".into(),
431 )))
432 } else {
433 Ok(args.remove(0))
434 }?;
435 Ok(Response::Enum(val))
436 }
437 _ => Err(NutError::UnknownResponseType(cmd_name).into()),
438 }
439 }
440
441 pub fn expect_ok(&self) -> crate::Result<&Response> {
442 match self {
443 Self::Ok => Ok(self),
444 _ => Err(NutError::UnexpectedResponse.into()),
445 }
446 }
447
448 pub fn expect_begin_list(self, expected_args: &[&str]) -> crate::Result<Response> {
449 let expected_args = shell_words::join(expected_args);
450 if let Self::BeginList(args) = &self {
451 if &expected_args == args {
452 Ok(self)
453 } else {
454 Err(NutError::UnexpectedResponse.into())
455 }
456 } else {
457 Err(NutError::UnexpectedResponse.into())
458 }
459 }
460
461 pub fn expect_var(&self) -> crate::Result<Variable> {
462 if let Self::Var(name, value) = &self {
463 Ok(Variable::parse(name, value.to_owned()))
464 } else {
465 Err(NutError::UnexpectedResponse.into())
466 }
467 }
468
469 pub fn expect_rw(&self) -> crate::Result<Variable> {
470 if let Self::Rw(name, value) = &self {
471 Ok(Variable::parse(name, value.to_owned()))
472 } else {
473 Err(NutError::UnexpectedResponse.into())
474 }
475 }
476
477 pub fn expect_ups(&self) -> crate::Result<(String, String)> {
478 if let Self::Ups(name, description) = &self {
479 Ok((name.to_owned(), description.to_owned()))
480 } else {
481 Err(NutError::UnexpectedResponse.into())
482 }
483 }
484
485 pub fn expect_client(&self) -> crate::Result<String> {
486 if let Self::Client(client_ip) = &self {
487 Ok(client_ip.to_owned())
488 } else {
489 Err(NutError::UnexpectedResponse.into())
490 }
491 }
492
493 pub fn expect_cmd(&self) -> crate::Result<String> {
494 if let Self::Cmd(name) = &self {
495 Ok(name.to_owned())
496 } else {
497 Err(NutError::UnexpectedResponse.into())
498 }
499 }
500
501 pub fn expect_cmddesc(&self) -> crate::Result<String> {
502 if let Self::CmdDesc(description) = &self {
503 Ok(description.to_owned())
504 } else {
505 Err(NutError::UnexpectedResponse.into())
506 }
507 }
508
509 pub fn expect_upsdesc(&self) -> crate::Result<String> {
510 if let Self::UpsDesc(description) = &self {
511 Ok(description.to_owned())
512 } else {
513 Err(NutError::UnexpectedResponse.into())
514 }
515 }
516
517 pub fn expect_desc(&self) -> crate::Result<String> {
518 if let Self::Desc(description) = &self {
519 Ok(description.to_owned())
520 } else {
521 Err(NutError::UnexpectedResponse.into())
522 }
523 }
524
525 pub fn expect_numlogins(&self) -> crate::Result<i32> {
526 if let Self::NumLogins(num) = &self {
527 Ok(*num)
528 } else {
529 Err(NutError::UnexpectedResponse.into())
530 }
531 }
532
533 pub fn expect_type(&self) -> crate::Result<VariableDefinition> {
534 if let Self::Type(name, types) = &self {
535 VariableDefinition::try_from((
536 name.to_owned(),
537 types.iter().map(String::as_str).collect(),
538 ))
539 } else {
540 Err(NutError::UnexpectedResponse.into())
541 }
542 }
543
544 pub fn expect_range(&self) -> crate::Result<VariableRange> {
545 if let Self::Range(range) = &self {
546 Ok(range.to_owned())
547 } else {
548 Err(NutError::UnexpectedResponse.into())
549 }
550 }
551
552 pub fn expect_enum(&self) -> crate::Result<String> {
553 if let Self::Enum(value) = &self {
554 Ok(value.to_owned())
555 } else {
556 Err(NutError::UnexpectedResponse.into())
557 }
558 }
559}
560
561macro_rules! implement_list_commands {
567 (
568 $(
569 $(#[$attr:meta])+
570 $vis:vis fn $name:ident($($argname:ident: $argty:ty),*) -> $retty:ty {
571 (
572 $query:block,
573 $mapper:block,
574 )
575 }
576 )*
577 ) => {
578 impl crate::blocking::Connection {
579 $(
580 $(#[$attr])*
581 #[allow(dead_code)]
582 $vis fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<$retty> {
583 match self {
584 Self::Tcp(conn) => {
585 conn.write_cmd(Command::List($query))?;
586 let list = conn.read_list($query)?;
587 list.into_iter().map($mapper).collect()
588 },
589 }
590 }
591 )*
592 }
593
594 #[cfg(feature = "async")]
595 impl crate::tokio::Connection {
596 $(
597 $(#[$attr])*
598 #[allow(dead_code)]
599 $vis async fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<$retty> {
600 match self {
601 Self::Tcp(conn) => {
602 conn.write_cmd(Command::List($query)).await?;
603 let list = conn.read_list($query).await?;
604 list.into_iter().map($mapper).collect()
605 },
606 }
607 }
608 )*
609 }
610 };
611}
612
613macro_rules! implement_get_commands {
619 (
620 $(
621 $(#[$attr:meta])+
622 $vis:vis fn $name:ident($($argname:ident: $argty:ty),*) -> $retty:ty {
623 (
624 $query:block,
625 $mapper:block,
626 )
627 }
628 )*
629 ) => {
630 impl crate::blocking::Connection {
631 $(
632 $(#[$attr])*
633 #[allow(dead_code)]
634 $vis fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<$retty> {
635 match self {
636 Self::Tcp(conn) => {
637 conn.write_cmd(Command::Get($query))?;
638 ($mapper)(conn.read_response()?)
639 },
640 }
641 }
642 )*
643 }
644
645 #[cfg(feature = "async")]
646 impl crate::tokio::Connection {
647 $(
648 $(#[$attr])*
649 #[allow(dead_code)]
650 $vis async fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<$retty> {
651 match self {
652 Self::Tcp(conn) => {
653 conn.write_cmd(Command::Get($query)).await?;
654 ($mapper)(conn.read_response().await?)
655 },
656 }
657 }
658 )*
659 }
660 };
661}
662
663macro_rules! implement_simple_commands {
669 (
670 $(
671 $(#[$attr:meta])+
672 $vis:vis fn $name:ident($($argname:ident: $argty:ty),*) -> $retty:ty {
673 (
674 $cmd:block,
675 $mapper:block,
676 )
677 }
678 )*
679 ) => {
680 impl crate::blocking::Connection {
681 $(
682 $(#[$attr])*
683 #[allow(dead_code)]
684 $vis fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<$retty> {
685 match self {
686 Self::Tcp(conn) => {
687 conn.write_cmd($cmd)?;
688 ($mapper)(conn.read_plain_response()?)
689 },
690 }
691 }
692 )*
693 }
694
695 #[cfg(feature = "async")]
696 impl crate::tokio::Connection {
697 $(
698 $(#[$attr])*
699 #[allow(dead_code)]
700 $vis async fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<$retty> {
701 match self {
702 Self::Tcp(conn) => {
703 conn.write_cmd($cmd).await?;
704 ($mapper)(conn.read_plain_response().await?)
705 },
706 }
707 }
708 )*
709 }
710 };
711}
712
713macro_rules! implement_action_commands {
717 (
718 $(
719 $(#[$attr:meta])+
720 $vis:vis fn $name:ident($($argname:ident: $argty:ty),*) $cmd:block
721 )*
722 ) => {
723 impl crate::blocking::Connection {
724 $(
725 $(#[$attr])*
726 #[allow(dead_code)]
727 $vis fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<()> {
728 match self {
729 Self::Tcp(conn) => {
730 conn.write_cmd($cmd)?;
731 conn.read_response()?.expect_ok()?;
732 Ok(())
733 },
734 }
735 }
736 )*
737 }
738
739 #[cfg(feature = "async")]
740 impl crate::tokio::Connection {
741 $(
742 $(#[$attr])*
743 #[allow(dead_code)]
744 $vis async fn $name(&mut self$(, $argname: $argty)*) -> crate::Result<()> {
745 match self {
746 Self::Tcp(conn) => {
747 conn.write_cmd($cmd).await?;
748 conn.read_response().await?.expect_ok()?;
749 Ok(())
750 },
751 }
752 }
753 )*
754 }
755 };
756}
757
758implement_list_commands! {
759 pub fn list_ups() -> Vec<(String, String)> {
761 (
762 { &["UPS"] },
763 { |row: Response| row.expect_ups() },
764 )
765 }
766
767 pub fn list_clients(ups_name: &str) -> Vec<String> {
769 (
770 { &["CLIENT", ups_name] },
771 { |row: Response| row.expect_client() },
772 )
773 }
774
775 pub fn list_vars(ups_name: &str) -> Vec<Variable> {
777 (
778 { &["VAR", ups_name] },
779 { |row: Response| row.expect_var() },
780 )
781 }
782
783 pub fn list_mutable_vars(ups_name: &str) -> Vec<Variable> {
785 (
786 { &["RW", ups_name] },
787 { |row: Response| row.expect_rw() },
788 )
789 }
790
791 pub fn list_commands(ups_name: &str) -> Vec<String> {
793 (
794 { &["CMD", ups_name] },
795 { |row: Response| row.expect_cmd() },
796 )
797 }
798
799 pub fn list_var_range(ups_name: &str, variable: &str) -> Vec<VariableRange> {
801 (
802 { &["RANGE", ups_name, variable] },
803 { |row: Response| row.expect_range() },
804 )
805 }
806
807 pub fn list_var_enum(ups_name: &str, variable: &str) -> Vec<String> {
809 (
810 { &["ENUM", ups_name, variable] },
811 { |row: Response| row.expect_enum() },
812 )
813 }
814}
815
816implement_get_commands! {
817 pub fn get_var(ups_name: &str, variable: &str) -> Variable {
819 (
820 { &["VAR", ups_name, variable] },
821 { |row: Response| row.expect_var() },
822 )
823 }
824
825 pub fn get_var_description(ups_name: &str, variable: &str) -> String {
827 (
828 { &["DESC", ups_name, variable] },
829 { |row: Response| row.expect_desc() },
830 )
831 }
832
833 pub fn get_var_type(ups_name: &str, variable: &str) -> VariableDefinition {
835 (
836 { &["TYPE", ups_name, variable] },
837 { |row: Response| row.expect_type() },
838 )
839 }
840
841 pub fn get_command_description(ups_name: &str, variable: &str) -> String {
843 (
844 { &["CMDDESC", ups_name, variable] },
845 { |row: Response| row.expect_cmddesc() },
846 )
847 }
848
849 pub fn get_ups_description(ups_name: &str) -> String {
851 (
852 { &["UPSDESC", ups_name] },
853 { |row: Response| row.expect_upsdesc() },
854 )
855 }
856
857 pub fn get_num_logins(ups_name: &str) -> i32 {
859 (
860 { &["NUMLOGINS", ups_name] },
861 { |row: Response| row.expect_numlogins() },
862 )
863 }
864}
865
866implement_simple_commands! {
867 pub fn get_network_version() -> String {
869 (
870 { Command::NetworkVersion },
871 { |row: String| Ok(row) },
872 )
873 }
874
875 pub fn get_server_version() -> String {
877 (
878 { Command::Version },
879 { |row: String| Ok(row) },
880 )
881 }
882}
883
884implement_action_commands! {
885 pub(crate) fn set_username(username: &str) {
887 Command::SetUsername(username)
888 }
889
890 pub(crate) fn set_password(password: &str) {
892 Command::SetPassword(password)
893 }
894
895 pub(crate) fn logout() {
897 Command::Logout
898 }
899}