1 use crate::pri::{compose_pri, SyslogFacility, SyslogSeverity}; 2 use crate::structured_data; 3 use crate::procid::ProcId; 4 use chrono::prelude::*; 5 use std::fmt; 6 7 8 #[derive(Clone, Debug, PartialEq, Eq)] 9 pub enum Protocol { 10 RFC3164, 11 RFC5424(u32), 12 } 13 14 #[derive(Clone, Debug)] 15 pub struct Message<S: AsRef<str> + Ord + PartialEq + Clone> { 16 pub protocol: Protocol, 17 pub facility: Option<SyslogFacility>, 18 pub severity: Option<SyslogSeverity>, 19 pub timestamp: Option<DateTime<FixedOffset>>, 20 pub hostname: Option<S>, 21 pub appname: Option<S>, 22 pub procid: Option<ProcId<S>>, 23 pub msgid: Option<S>, 24 pub structured_data: Vec<structured_data::StructuredElement<S>>, 25 pub msg: S, 26 } 27 28 impl<S: AsRef<str> + Ord + PartialEq + Clone> fmt::Display for Message<S> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 let empty = "-".to_string(); 31 32 write!( 33 f, 34 "<{}>{} {} {} ", 35 compose_pri( 36 self.facility.unwrap_or(SyslogFacility::LOG_SYSLOG), 37 self.severity.unwrap_or(SyslogSeverity::SEV_DEBUG) 38 ), 39 match self.protocol { 40 Protocol::RFC3164 => "".to_string(), 41 Protocol::RFC5424(version) => version.to_string() 42 }, 43 self.timestamp.unwrap_or(Utc::now().into()).to_rfc3339(), 44 self.hostname 45 .as_ref() 46 .map(|s| s.as_ref()) 47 .unwrap_or(&empty) 48 )?; 49 50 match self.protocol { 51 Protocol::RFC5424(_) => { 52 write!(f, "{} ", self.appname 53 .as_ref() 54 .map(|s| s.as_ref()) 55 .unwrap_or(&empty))?; 56 match &self.procid { 57 None => write!(f, "- ")?, 58 Some(procid) => write!(f, "{} ", procid)?, 59 }; 60 } 61 Protocol::RFC3164 => 62 match (&self.appname, &self.procid) { 63 (Some(appname), Some(procid)) => 64 write!(f, "{}[{}]: ", appname.as_ref(), procid)?, 65 (Some(appname), None) => 66 write!(f, "{}: ", appname.as_ref())?, 67 _ => 68 write!(f, ": ")?, 69 } 70 } 71 72 73 if let Protocol::RFC5424(_) = self.protocol { 74 write!(f, "{} ", self.msgid 75 .as_ref() 76 .map(|s| s.as_ref()) 77 .unwrap_or(&empty))?; 78 } 79 80 if self.structured_data.len() == 0 { 81 if let Protocol::RFC5424(_) = self.protocol { 82 write!(f, "- ")?; 83 } 84 } else { 85 for elem in &self.structured_data { 86 write!(f, "{}", elem)?; 87 } 88 write!(f, " ")?; 89 } 90 91 write!(f, "{}", self.msg.as_ref()) 92 } 93 } 94 95 impl<S: AsRef<str> + Ord + Clone> PartialEq for Message<S> { eq(&self, other: &Self) -> bool96 fn eq(&self, other: &Self) -> bool { 97 self.facility == other.facility 98 && self.severity == other.severity 99 && self.timestamp == other.timestamp 100 && self.hostname == other.hostname 101 && self.appname == other.appname 102 && self.procid == other.procid 103 && self.msgid == other.msgid 104 && self.structured_data == other.structured_data 105 && self.msg == other.msg 106 } 107 } 108 109 impl From<Message<&str>> for Message<String> { from(message: Message<&str>) -> Self110 fn from(message: Message<&str>) -> Self { 111 Message { 112 facility: message.facility, 113 severity: message.severity, 114 timestamp: message.timestamp, 115 hostname: message.hostname.map(|s| s.to_string()), 116 appname: message.appname.map(|s| s.to_string()), 117 procid: message.procid.map(|s| s.into()), 118 msgid: message.msgid.map(|s| s.to_string()), 119 protocol: message.protocol, 120 structured_data: message 121 .structured_data 122 .iter() 123 .map(|e| e.clone().into()) 124 .collect(), 125 msg: message.msg.to_string(), 126 } 127 } 128 } 129 130 131 132