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