kanidm_proto/
messages.rs

1// User-facing output things
2
3use std::fmt;
4use std::str::FromStr;
5
6use serde::{Deserialize, Serialize};
7
8/// This is used in user-facing CLIs to set the formatting for output,
9/// and defaults to text.
10#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
11#[serde(rename_all = "lowercase")]
12pub enum ConsoleOutputMode {
13    #[default]
14    Text,
15    JSON,
16}
17
18impl FromStr for ConsoleOutputMode {
19    type Err = &'static str;
20
21    /// This can be safely unwrap'd because it'll always return a default of text
22    /// ```
23    /// use kanidm_proto::messages::ConsoleOutputMode;
24    ///
25    /// let mode: ConsoleOutputMode = "🦀".into();
26    /// assert_eq!(ConsoleOutputMode::Text, mode);
27    /// let mode: ConsoleOutputMode = "".into();
28    /// assert_eq!(ConsoleOutputMode::Text, mode);
29    ///
30    /// let mode: ConsoleOutputMode = "json".into();
31    /// assert_eq!(ConsoleOutputMode::JSON, mode);
32    /// ```
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        match s {
35            "json" => Ok(ConsoleOutputMode::JSON),
36            "text" => Ok(ConsoleOutputMode::Text),
37            _ => {
38                eprintln!("Supplied output mode ({s:?}) was invalid, defaulting to text");
39                Ok(ConsoleOutputMode::Text)
40            }
41        }
42    }
43}
44
45/// This will take any string, if it's 'text' or 'json' then you'll get
46/// what you asked for, else you'll get a text version.
47///
48/// ```
49/// use kanidm_proto::messages::ConsoleOutputMode;
50/// let bork = "text";
51/// let com: ConsoleOutputMode = bork.into();
52/// matches!(ConsoleOutputMode::Text, com);
53/// ```
54impl From<&str> for ConsoleOutputMode {
55    fn from(input: &str) -> Self {
56        match ConsoleOutputMode::from_str(input) {
57            Ok(val) => val,
58            Err(_) => Self::Text,
59        }
60    }
61}
62
63/// This will take any string, if it's 'text' or 'json' then you'll get
64/// what you asked for, else you'll get a text version.
65///
66/// ```
67/// use kanidm_proto::messages::ConsoleOutputMode;
68/// let bork = String::from("cr4bz");
69/// let com: ConsoleOutputMode = bork.into();
70/// matches!(ConsoleOutputMode::Text, com);
71/// ```
72impl From<String> for ConsoleOutputMode {
73    fn from(input: String) -> Self {
74        match ConsoleOutputMode::from_str(input.as_str()) {
75            Ok(val) => val,
76            Err(_) => Self::Text,
77        }
78    }
79}
80
81#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
82#[serde(rename_all = "lowercase")]
83pub enum MessageStatus {
84    Failure,
85    Success,
86}
87
88impl fmt::Display for MessageStatus {
89    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
90        match *self {
91            MessageStatus::Failure => f.write_str("failure"),
92            MessageStatus::Success => f.write_str("success"),
93        }
94    }
95}
96
97#[derive(Debug, Serialize, Deserialize)]
98pub struct AccountChangeMessage {
99    #[serde(skip_serializing)]
100    pub output_mode: ConsoleOutputMode,
101    pub action: String,
102    pub result: String,
103    pub status: MessageStatus,
104    pub src_user: String,
105    pub dest_user: String,
106}
107
108impl Default for AccountChangeMessage {
109    fn default() -> Self {
110        AccountChangeMessage {
111            output_mode: ConsoleOutputMode::Text,
112            action: String::from(""),
113            result: String::from(""),
114            status: MessageStatus::Success,
115            src_user: String::from(""),
116            dest_user: String::from(""),
117        }
118    }
119}
120
121/// This outputs in either JSON or Text depending on the output_mode setting
122/// ```
123/// use std::fmt::format;
124/// use kanidm_proto::messages::*;
125/// let mut msg = AccountChangeMessage::default();
126/// msg.action=String::from("cake_eating");
127/// msg.src_user=String::from("Kani");
128/// msg.dest_user=String::from("Krabby");
129/// msg.result=String::from("It was amazing");
130/// assert_eq!(msg.status, MessageStatus::Success);
131///
132/// let expected_result = "success - cake_eating for Krabby: It was amazing";
133/// assert_eq!(format!("{}", msg), expected_result);
134///
135/// msg.output_mode = ConsoleOutputMode::JSON;
136/// let expected_result = "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\",\"src_user\":\"Kani\",\"dest_user\":\"Krabby\"}";
137/// assert_eq!(format!("{}", msg), expected_result);
138/// ```
139impl fmt::Display for AccountChangeMessage {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self.output_mode {
142            ConsoleOutputMode::JSON => write!(
143                f,
144                "{}",
145                serde_json::to_string(self).unwrap_or(format!("{self:?}")) /* if it fails to JSON serialize, just debug-dump it */
146            ),
147            ConsoleOutputMode::Text => write!(
148                f,
149                "{} - {} for {}: {}",
150                self.status, self.action, self.dest_user, self.result,
151            ),
152        }
153    }
154}
155
156#[derive(Debug, Serialize, Deserialize)]
157pub struct BasicMessage {
158    #[serde(skip_serializing)]
159    pub output_mode: ConsoleOutputMode,
160    pub action: String,
161    pub result: String,
162    pub status: MessageStatus,
163}
164
165impl Default for BasicMessage {
166    fn default() -> Self {
167        BasicMessage {
168            output_mode: ConsoleOutputMode::Text,
169            action: String::from(""),
170            result: String::from(""),
171            status: MessageStatus::Success,
172        }
173    }
174}
175
176/// This outputs in either JSON or Text depending on the output_mode setting
177/// ```
178/// use kanidm_proto::messages::*;
179/// use std::fmt::format;
180/// let mut msg = BasicMessage::default();
181/// msg.action = String::from("cake_eating");
182/// msg.result = String::from("It was amazing");
183/// assert_eq!(msg.status, MessageStatus::Success);
184///
185/// let expected_result = "success - cake_eating: It was amazing";
186/// assert_eq!(format!("{}", msg), expected_result);
187///
188/// msg.output_mode = ConsoleOutputMode::JSON;
189/// let expected_result =
190///     "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\"}";
191/// assert_eq!(format!("{}", msg), expected_result);
192/// ```
193impl fmt::Display for BasicMessage {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        match self.output_mode {
196            ConsoleOutputMode::JSON => write!(
197                f,
198                "{}",
199                serde_json::to_string(self).unwrap_or(format!("{self:?}")) /* if it fails to JSON serialize, just debug-dump it */
200            ),
201            ConsoleOutputMode::Text => {
202                write!(f, "{} - {}: {}", self.status, self.action, self.result,)
203            }
204        }
205    }
206}