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
use crate::pri::{compose_pri, SyslogFacility, SyslogSeverity};
use crate::procid::ProcId;
use crate::structured_data;
use chrono::prelude::*;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Protocol {
    RFC3164,
    RFC5424(u32),
}

#[derive(Clone, Debug)]
pub struct Message<S: AsRef<str> + Ord + PartialEq + Clone> {
    pub protocol: Protocol,
    pub facility: Option<SyslogFacility>,
    pub severity: Option<SyslogSeverity>,
    pub timestamp: Option<DateTime<FixedOffset>>,
    pub hostname: Option<S>,
    pub appname: Option<S>,
    pub procid: Option<ProcId<S>>,
    pub msgid: Option<S>,
    pub structured_data: Vec<structured_data::StructuredElement<S>>,
    pub msg: S,
}

impl<S: AsRef<str> + Ord + PartialEq + Clone> fmt::Display for Message<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let empty = "-".to_string();

        write!(
            f,
            "<{}>{} {} {} ",
            compose_pri(
                self.facility.unwrap_or(SyslogFacility::LOG_SYSLOG),
                self.severity.unwrap_or(SyslogSeverity::SEV_DEBUG)
            ),
            match self.protocol {
                Protocol::RFC3164 => "".to_string(),
                Protocol::RFC5424(version) => version.to_string(),
            },
            self.timestamp.unwrap_or(Utc::now().into()).to_rfc3339(),
            self.hostname.as_ref().map(|s| s.as_ref()).unwrap_or(&empty)
        )?;

        match self.protocol {
            Protocol::RFC5424(_) => {
                write!(
                    f,
                    "{} ",
                    self.appname.as_ref().map(|s| s.as_ref()).unwrap_or(&empty)
                )?;
                match &self.procid {
                    None => write!(f, "- ")?,
                    Some(procid) => write!(f, "{} ", procid)?,
                };
            }
            Protocol::RFC3164 => match (&self.appname, &self.procid) {
                (Some(appname), Some(procid)) => write!(f, "{}[{}]: ", appname.as_ref(), procid)?,
                (Some(appname), None) => write!(f, "{}: ", appname.as_ref())?,
                _ => write!(f, ": ")?,
            },
        }

        if let Protocol::RFC5424(_) = self.protocol {
            write!(
                f,
                "{} ",
                self.msgid.as_ref().map(|s| s.as_ref()).unwrap_or(&empty)
            )?;
        }

        if self.structured_data.len() == 0 {
            if let Protocol::RFC5424(_) = self.protocol {
                write!(f, "- ")?;
            }
        } else {
            for elem in &self.structured_data {
                write!(f, "{}", elem)?;
            }
            write!(f, " ")?;
        }

        write!(f, "{}", self.msg.as_ref())
    }
}

impl<S: AsRef<str> + Ord + Clone> PartialEq for Message<S> {
    fn eq(&self, other: &Self) -> bool {
        self.facility == other.facility
            && self.severity == other.severity
            && self.timestamp == other.timestamp
            && self.hostname == other.hostname
            && self.appname == other.appname
            && self.procid == other.procid
            && self.msgid == other.msgid
            && self.structured_data == other.structured_data
            && self.msg == other.msg
    }
}

impl From<Message<&str>> for Message<String> {
    fn from(message: Message<&str>) -> Self {
        Message {
            facility: message.facility,
            severity: message.severity,
            timestamp: message.timestamp,
            hostname: message.hostname.map(|s| s.to_string()),
            appname: message.appname.map(|s| s.to_string()),
            procid: message.procid.map(|s| s.into()),
            msgid: message.msgid.map(|s| s.to_string()),
            protocol: message.protocol,
            structured_data: message
                .structured_data
                .iter()
                .map(|e| e.clone().into())
                .collect(),
            msg: message.msg.to_string(),
        }
    }
}