syslog_rs/formatters/
syslog_3146.rs

1/*-
2 * syslog-rs - a syslog client translated from libc to rust
3 * 
4 * Copyright 2025 Aleksandr Morozov
5 * 
6 * The syslog-rs crate can be redistributed and/or modified
7 * under the terms of either of the following licenses:
8 *
9 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
10 *
11 *   2. The MIT License (MIT)
12 *                     
13 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
14 */
15
16use std::borrow::Cow;
17
18use chrono::Local;
19
20use crate::{CBRACE_SEM, OBRACE, Priority, RFC3164_MAX_PAYLOAD_LEN, SyslogMsgPriFac, WSPACE, truncate, truncate_n};
21
22use super::{SyslogFormatted, SyslogFormatter};
23
24
25
26#[derive(Debug, Clone)]
27#[repr(transparent)]
28pub struct FormatRfc3146(Cow<'static, str>);
29
30unsafe impl Send for FormatRfc3146 {}
31
32impl From<String> for FormatRfc3146
33{
34    fn from(value: String) -> FormatRfc3146 
35    {
36        return Self(Cow::Owned(value));
37    }
38}
39
40impl From<&'static str> for FormatRfc3146
41{
42    fn from(value: &'static str) -> FormatRfc3146
43    {
44        return FormatRfc3146(Cow::Borrowed(value));
45    }
46}
47
48
49
50impl SyslogFormatter for FormatRfc3146
51{
52    fn vsyslog1_format(&self, _max_msg_size: usize, prifac: SyslogMsgPriFac, progname: &str, pid: Option<&str>) -> SyslogFormatted
53    {
54        // get timedate
55        let timedate = Local::now().format("%h %e %T").to_string();
56
57        let msg_payload = truncate_n(&self.0, RFC3164_MAX_PAYLOAD_LEN);
58
59        let msg_payload_final = 
60            if msg_payload.ends_with("\n") == true
61            {
62                truncate(&msg_payload)
63            }
64            else
65            {
66                &msg_payload
67            };
68            
69        // message based on RFC 3164
70        let msg_pri = 
71            [
72                "<", prifac.get_val().to_string().as_str(), ">"
73            ]
74            .concat();
75
76        let msg_pid =
77            if let Some(p) = pid
78            {
79                [OBRACE, p, CBRACE_SEM].concat()
80            }
81            else
82            {
83                [""].concat()
84            };
85
86        let msg_pkt = 
87            [
88                //pri <>
89                //Cow::Owned(msg_pri), 
90                // timedate
91                timedate.as_str(),
92                // hostname
93                // " ".as_bytes(), hostname.as_bytes(), 
94                // appname
95                WSPACE, progname,
96                // PID
97                msg_pid.as_str(),
98                // msg
99                WSPACE, /*b"\xEF\xBB\xBF",*/ msg_payload_final
100            ]
101            .concat();
102
103
104        return 
105            SyslogFormatted
106            { 
107                msg_header: Some(msg_pri), 
108                msg_payload: msg_pkt, 
109                full_msg: None
110            };
111    }
112}
113
114#[cfg(test)]
115mod test_rfc3146
116{
117    use crate::LogFacility;
118
119    use super::*;
120
121    #[test]
122    fn test_1()
123    {
124        let msg = FormatRfc3146::from("test string!");
125        let prifac = SyslogMsgPriFac::set_facility(Priority::LOG_ERR, LogFacility::LOG_DAEMON);
126        let mut res = msg.vsyslog1_format(1024, prifac, "testprog", Some("102"));
127
128        assert_eq!(res.msg_header.as_ref().map(|f| f.as_str()), Some("<27>"));
129
130        let fullmsg = res.get_full_msg();
131
132        println!("{}", fullmsg);
133
134        assert_eq!(fullmsg.find("testprog"), Some(20));
135
136        assert_eq!(fullmsg.find("[102]"), Some(28));
137
138        assert_eq!(fullmsg.find("]:"), Some(32));
139
140        assert_eq!(fullmsg.find("test string!"), Some(35));
141    }
142}