syslog-rs 6.5.0

A native Rust implementation of the glibc/libc/windows syslog client and windows native log for logging.
Documentation
/*-
 * syslog-rs - a syslog client translated from libc to rust
 * 
 * Copyright 2025 Aleksandr Morozov
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

use std::borrow::Cow;

use chrono::Local;

use crate::{CBRACE_SEM, OBRACE, Priority, RFC3164_MAX_PAYLOAD_LEN, SyslogMsgPriFac, WSPACE, truncate, truncate_n};

use super::{SyslogFormatted, SyslogFormatter};



#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct FormatRfc3146(Cow<'static, str>);

unsafe impl Send for FormatRfc3146 {}

impl From<String> for FormatRfc3146
{
    fn from(value: String) -> FormatRfc3146 
    {
        return Self(Cow::Owned(value));
    }
}

impl From<&'static str> for FormatRfc3146
{
    fn from(value: &'static str) -> FormatRfc3146
    {
        return FormatRfc3146(Cow::Borrowed(value));
    }
}



impl SyslogFormatter for FormatRfc3146
{
    fn vsyslog1_format(&self, _max_msg_size: usize, prifac: SyslogMsgPriFac, progname: &str, pid: Option<&str>) -> SyslogFormatted
    {
        // get timedate
        let timedate = Local::now().format("%h %e %T").to_string();

        let msg_payload = truncate_n(&self.0, RFC3164_MAX_PAYLOAD_LEN);

        let msg_payload_final = 
            if msg_payload.ends_with("\n") == true
            {
                truncate(&msg_payload)
            }
            else
            {
                &msg_payload
            };
            
        // message based on RFC 3164
        let msg_pri = 
            [
                "<", prifac.get_val().to_string().as_str(), ">"
            ]
            .concat();

        let msg_pid =
            if let Some(p) = pid
            {
                [OBRACE, p, CBRACE_SEM].concat()
            }
            else
            {
                [""].concat()
            };

        let msg_pkt = 
            [
                //pri <>
                //Cow::Owned(msg_pri), 
                // timedate
                timedate.as_str(),
                // hostname
                // " ".as_bytes(), hostname.as_bytes(), 
                // appname
                WSPACE, progname,
                // PID
                msg_pid.as_str(),
                // msg
                WSPACE, /*b"\xEF\xBB\xBF",*/ msg_payload_final
            ]
            .concat();


        return 
            SyslogFormatted
            { 
                msg_header: Some(msg_pri), 
                msg_payload: msg_pkt, 
                full_msg: None
            };
    }
}

#[cfg(test)]
mod test_rfc3146
{
    use crate::LogFacility;

    use super::*;

    #[test]
    fn test_1()
    {
        let msg = FormatRfc3146::from("test string!");
        let prifac = SyslogMsgPriFac::set_facility(Priority::LOG_ERR, LogFacility::LOG_DAEMON);
        let mut res = msg.vsyslog1_format(1024, prifac, "testprog", Some("102"));

        assert_eq!(res.msg_header.as_ref().map(|f| f.as_str()), Some("<27>"));

        let fullmsg = res.get_full_msg();

        println!("{}", fullmsg);

        assert_eq!(fullmsg.find("testprog"), Some(20));

        assert_eq!(fullmsg.find("[102]"), Some(28));

        assert_eq!(fullmsg.find("]:"), Some(32));

        assert_eq!(fullmsg.find("test string!"), Some(35));
    }
}