1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// User-facing output things

use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// This is used in user-facing CLIs to set the formatting for output,
/// and defaults to text.
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum ConsoleOutputMode {
    #[default]
    Text,
    JSON,
}

impl FromStr for ConsoleOutputMode {
    type Err = &'static str;

    /// This can be safely unwrap'd because it'll always return a default of text
    /// ```
    /// use kanidm_proto::messages::ConsoleOutputMode;
    ///
    /// let mode: ConsoleOutputMode = "🦀".into();
    /// assert_eq!(ConsoleOutputMode::Text, mode);
    /// let mode: ConsoleOutputMode = "".into();
    /// assert_eq!(ConsoleOutputMode::Text, mode);
    ///
    /// let mode: ConsoleOutputMode = "json".into();
    /// assert_eq!(ConsoleOutputMode::JSON, mode);
    /// ```
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "json" => Ok(ConsoleOutputMode::JSON),
            "text" => Ok(ConsoleOutputMode::Text),
            _ => {
                eprintln!(
                    "Supplied output mode ({:?}) was invalid, defaulting to text",
                    s
                );
                Ok(ConsoleOutputMode::Text)
            }
        }
    }
}

/// This will take any string, if it's 'text' or 'json' then you'll get
/// what you asked for, else you'll get a text version.
///
/// ```
/// use kanidm_proto::messages::ConsoleOutputMode;
/// let bork = "text";
/// let com: ConsoleOutputMode = bork.into();
/// matches!(ConsoleOutputMode::Text, com);
/// ```
impl From<&str> for ConsoleOutputMode {
    fn from(input: &str) -> Self {
        match ConsoleOutputMode::from_str(input) {
            Ok(val) => val,
            Err(_) => Self::Text,
        }
    }
}

/// This will take any string, if it's 'text' or 'json' then you'll get
/// what you asked for, else you'll get a text version.
///
/// ```
/// use kanidm_proto::messages::ConsoleOutputMode;
/// let bork = String::from("cr4bz");
/// let com: ConsoleOutputMode = bork.into();
/// matches!(ConsoleOutputMode::Text, com);
/// ```
impl From<String> for ConsoleOutputMode {
    fn from(input: String) -> Self {
        match ConsoleOutputMode::from_str(input.as_str()) {
            Ok(val) => val,
            Err(_) => Self::Text,
        }
    }
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum MessageStatus {
    Failure,
    Success,
}

impl fmt::Display for MessageStatus {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
        match *self {
            MessageStatus::Failure => f.write_str("failure"),
            MessageStatus::Success => f.write_str("success"),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AccountChangeMessage {
    #[serde(skip_serializing)]
    pub output_mode: ConsoleOutputMode,
    pub action: String,
    pub result: String,
    pub status: MessageStatus,
    pub src_user: String,
    pub dest_user: String,
}

impl Default for AccountChangeMessage {
    fn default() -> Self {
        AccountChangeMessage {
            output_mode: ConsoleOutputMode::Text,
            action: String::from(""),
            result: String::from(""),
            status: MessageStatus::Success,
            src_user: String::from(""),
            dest_user: String::from(""),
        }
    }
}

/// This outputs in either JSON or Text depending on the output_mode setting
/// ```
/// use std::fmt::format;
/// use kanidm_proto::messages::*;
/// let mut msg = AccountChangeMessage::default();
/// msg.action=String::from("cake_eating");
/// msg.src_user=String::from("Kani");
/// msg.dest_user=String::from("Krabby");
/// msg.result=String::from("It was amazing");
/// assert_eq!(msg.status, MessageStatus::Success);
///
/// let expected_result = "success - cake_eating for Krabby: It was amazing";
/// assert_eq!(format!("{}", msg), expected_result);
///
/// msg.output_mode = ConsoleOutputMode::JSON;
/// let expected_result = "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\",\"src_user\":\"Kani\",\"dest_user\":\"Krabby\"}";
/// assert_eq!(format!("{}", msg), expected_result);
/// ```
impl fmt::Display for AccountChangeMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.output_mode {
            ConsoleOutputMode::JSON => write!(
                f,
                "{}",
                serde_json::to_string(self).unwrap_or(format!("{:?}", self)) /* if it fails to JSON serialize, just debug-dump it */
            ),
            ConsoleOutputMode::Text => write!(
                f,
                "{} - {} for {}: {}",
                self.status, self.action, self.dest_user, self.result,
            ),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct BasicMessage {
    #[serde(skip_serializing)]
    pub output_mode: ConsoleOutputMode,
    pub action: String,
    pub result: String,
    pub status: MessageStatus,
}

impl Default for BasicMessage {
    fn default() -> Self {
        BasicMessage {
            output_mode: ConsoleOutputMode::Text,
            action: String::from(""),
            result: String::from(""),
            status: MessageStatus::Success,
        }
    }
}

/// This outputs in either JSON or Text depending on the output_mode setting
/// ```
/// use kanidm_proto::messages::*;
/// use std::fmt::format;
/// let mut msg = BasicMessage::default();
/// msg.action = String::from("cake_eating");
/// msg.result = String::from("It was amazing");
/// assert_eq!(msg.status, MessageStatus::Success);
///
/// let expected_result = "success - cake_eating: It was amazing";
/// assert_eq!(format!("{}", msg), expected_result);
///
/// msg.output_mode = ConsoleOutputMode::JSON;
/// let expected_result =
///     "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\"}";
/// assert_eq!(format!("{}", msg), expected_result);
/// ```
impl fmt::Display for BasicMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.output_mode {
            ConsoleOutputMode::JSON => write!(
                f,
                "{}",
                serde_json::to_string(self).unwrap_or(format!("{:?}", self)) /* if it fails to JSON serialize, just debug-dump it */
            ),
            ConsoleOutputMode::Text => {
                write!(f, "{} - {}: {}", self.status, self.action, self.result,)
            }
        }
    }
}