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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! Formatter to convert a message into a valid syslog message for the [5425](https://datatracker.ietf.org/doc/html/rfc5424) syslog protocol.
//!
//! This crate does not provide a transport method to get the message to the syslog daemon.
//! The focus is to correctly format a message ready for transport.

use core::{fmt, marker::PhantomData};
pub mod v5424;

/// The Priority value is calculated by first multiplying the Facility
/// number by 8 and then adding the numerical value of the Severity.
///
/// For example, a kernel message (Facility=0) with a Severity of Emergency
/// (Severity=0) would have a Priority value of 0. A "local use 4"
/// message (Facility=20) with a Severity of Notice (Severity=5) would
/// have a Priority value of 165.
///
/// [spec](https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1)
type Priority = u8;

/// The facility argument is used to specify what type of program is logging the message.
/// This lets the configuration file specify that messages from different facilities will be handled differently.
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum Facility {
    /// kernel messages
    Kern = 0 << 3,
    /// user-level messages
    User = 1 << 3,
    /// mail system
    Mail = 2 << 3,
    /// system daemons
    Daemon = 3 << 3,
    /// security/authorization messages
    Auth = 4 << 3,
    /// messages generated internally by syslogd
    Syslog = 5 << 3,
    /// line printer subsystem
    Lpr = 6 << 3,
    /// network news subsystem
    News = 7 << 3,
    /// UUCP subsystem
    Uucp = 8 << 3,
    /// clock daemon
    Cron = 9 << 3,
    /// security/authorization messages
    Authpriv = 10 << 3,
    /// FTP daemon
    Ftp = 11 << 3,
    /// local use 0  (local0)
    Local0 = 16 << 3,
    /// local use 1  (local1)
    Local1 = 17 << 3,
    /// local use 2  (local2)
    Local2 = 18 << 3,
    /// local use 3  (local3)
    Local3 = 19 << 3,
    /// local use 4  (local4)
    Local4 = 20 << 3,
    /// local use 5  (local5)
    Local5 = 21 << 3,
    /// local use 6  (local6)
    Local6 = 22 << 3,
    /// local use 7  (local7)
    Local7 = 23 << 3,
}

impl Default for Facility {
    fn default() -> Self {
        Self::Local0
    }
}

impl fmt::Display for Facility {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Facility::Kern => "Kern",
            Facility::User => "User",
            Facility::Mail => "Mail",
            Facility::Daemon => "Daemon",
            Facility::Auth => "Auth",
            Facility::Syslog => "Syslog",
            Facility::Lpr => "Lpr",
            Facility::News => "News",
            Facility::Uucp => "Uucp",
            Facility::Cron => "Cron",
            Facility::Authpriv => "Authpriv",
            Facility::Ftp => "Ftp",
            Facility::Local0 => "Local0",
            Facility::Local1 => "Local1",
            Facility::Local2 => "Local2",
            Facility::Local3 => "Local3",
            Facility::Local4 => "Local4",
            Facility::Local5 => "Local5",
            Facility::Local6 => "Local6",
            Facility::Local7 => "Local7",
        };

        f.write_str(s)
    }
}

impl<T> fmt::Display for IntToEnumError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let enum_name: &'static str = std::any::type_name::<T>();
        write!(f, "Failed to convert {} to {}", self.value, enum_name)
    }
}

impl TryFrom<u8> for Facility {
    type Error = IntToEnumError<Self>;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Into::<i32>::into(value).try_into()
    }
}

/// Try convert a i32 (libc::c_int) into a Facility
impl TryFrom<i32> for Facility {
    type Error = IntToEnumError<Self>;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        let variant = match value {
            0 => Self::Kern,
            1 => Self::User,
            2 => Self::Mail,
            3 => Self::Daemon,
            4 => Self::Auth,
            5 => Self::Syslog,
            6 => Self::Lpr,
            7 => Self::News,
            8 => Self::Uucp,
            9 => Self::Cron,
            10 => Self::Authpriv,
            11 => Self::Ftp,
            16 => Self::Local0,
            17 => Self::Local1,
            18 => Self::Local2,
            19 => Self::Local3,
            20 => Self::Local4,
            21 => Self::Local5,
            22 => Self::Local6,
            23 => Self::Local7,
            _ => {
                return Err(IntToEnumError {
                    value,
                    target: PhantomData,
                })
            }
        };

        Ok(variant)
    }
}

/// The severity of the message
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum Severity {
    /// System is unusable.
    /// For example: a panic condition.
    Emerg,
    /// Action must be taken immediately.
    /// For example: A condition that should be corrected immediately, such as a corrupted system database.
    Alert,
    /// Critical conditions
    /// For example: Hard device errors
    Crit,
    /// Error conditions.
    Err,
    /// Warning conditions.
    Warning,
    /// Normal but significant condition.
    /// For example: Conditions that are not error conditions, but that may require special handling.
    Notice,
    /// Informational messages.
    /// For example: Confirmation that the program is working as expected.
    Info,
    /// Debug-level messages.
    /// For example: Messages that contain information normally of use only when debugging a program.
    Debug,
}

impl fmt::Display for Severity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Severity::Emerg => "Emerg",
            Severity::Alert => "Alert",
            Severity::Crit => "Crit",
            Severity::Err => "Err",
            Severity::Warning => "Warning",
            Severity::Notice => "Notice",
            Severity::Info => "Info",
            Severity::Debug => "Debug",
        };

        f.write_str(s)
    }
}

impl TryFrom<u8> for Severity {
    type Error = IntToEnumError<Self>;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Into::<i32>::into(value).try_into()
    }
}

/// Try convert a i32 (libc::c_int) into a Severity
impl TryFrom<i32> for Severity {
    type Error = IntToEnumError<Self>;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        let variant = match value {
            0 => Self::Emerg,
            1 => Self::Alert,
            2 => Self::Crit,
            3 => Self::Err,
            4 => Self::Warning,
            5 => Self::Notice,
            6 => Self::Info,
            7 => Self::Debug,
            _ => {
                return Err(IntToEnumError {
                    value,
                    target: PhantomData,
                })
            }
        };

        Ok(variant)
    }
}

/// Error returned if converting from an integer to a u8 based enum fails
pub struct IntToEnumError<T> {
    value: i32,
    target: PhantomData<T>,
}

impl<T> fmt::Debug for IntToEnumError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("IntToEnumError")
            .field("value", &self.value)
            .field("target", &std::any::type_name::<T>())
            .finish()
    }
}