1use std::io::IsTerminal;
2
3#[derive(Clone, Copy)]
8pub struct OutputConfig {
9 pub json: bool,
10 pub quiet: bool,
11}
12
13impl OutputConfig {
14 pub fn new(json_flag: bool, quiet: bool) -> Self {
15 let json = json_flag || !std::io::stdout().is_terminal();
16 Self { json, quiet }
17 }
18
19 pub fn print_data(&self, data: &str) {
21 println!("{data}");
22 }
23
24 pub fn print_message(&self, msg: &str) {
26 if !self.quiet {
27 eprintln!("{msg}");
28 }
29 }
30
31 pub fn print_result(&self, json_value: &serde_json::Value, human_message: &str) {
34 if self.json {
35 println!(
36 "{}",
37 serde_json::to_string_pretty(json_value).expect("failed to serialize JSON")
38 );
39 } else {
40 self.print_message(human_message);
41 }
42 }
43}
44
45pub mod exit_codes {
48 pub const SUCCESS: i32 = 0;
49 pub const CONFIG_ERROR: i32 = 2;
50 pub const AUTH_ERROR: i32 = 3;
51 pub const NOT_FOUND: i32 = 4;
52 pub const API_ERROR: i32 = 5;
53 pub const GENERAL_ERROR: i32 = 1;
54}
55
56pub fn exit_code_for_error(err: &(dyn std::error::Error + 'static)) -> i32 {
58 if let Some(api_err) = err.downcast_ref::<crate::api::ApiError>() {
59 match api_err {
60 crate::api::ApiError::Auth(_) => exit_codes::AUTH_ERROR,
61 crate::api::ApiError::NotFound(_) => exit_codes::NOT_FOUND,
62 crate::api::ApiError::Api { .. } => exit_codes::API_ERROR,
63 crate::api::ApiError::Http(_) | crate::api::ApiError::Other(_) => {
64 exit_codes::GENERAL_ERROR
65 }
66 }
67 } else {
68 exit_codes::GENERAL_ERROR
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::api::ApiError;
76
77 #[test]
78 fn exit_code_for_auth_error() {
79 let err = ApiError::Auth("bad key".into());
80 assert_eq!(exit_code_for_error(&err), exit_codes::AUTH_ERROR);
81 }
82
83 #[test]
84 fn exit_code_for_not_found() {
85 let err = ApiError::NotFound("Client with MAC aa:bb".into());
86 assert_eq!(exit_code_for_error(&err), exit_codes::NOT_FOUND);
87 }
88
89 #[test]
90 fn exit_code_for_api_error() {
91 let err = ApiError::Api {
92 status: 500,
93 message: "Internal Server Error".into(),
94 };
95 assert_eq!(exit_code_for_error(&err), exit_codes::API_ERROR);
96 }
97
98 #[test]
99 fn exit_code_for_other_error() {
100 let err = ApiError::Other("something".into());
101 assert_eq!(exit_code_for_error(&err), exit_codes::GENERAL_ERROR);
102 }
103
104 #[test]
105 fn exit_code_for_non_api_error() {
106 let err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
107 assert_eq!(exit_code_for_error(&err), exit_codes::GENERAL_ERROR);
108 }
109}