Skip to main content

syslog_rs/
common.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
16
17
18
19use std::{borrow::Cow, fmt, ops::{BitAnd, Shl}, path::Path, sync::LazyLock};
20
21use crate::{error::{SyRes, SyslogError}, map_error_code, portable, throw_error};
22
23#[cfg(target_family = "windows")]
24pub use self::common_eventlog_items::*;
25
26#[cfg(target_family = "unix")]
27pub use self::common_syslog_items::*;
28
29#[cfg(target_family = "windows")]
30pub mod common_eventlog_items
31{
32    use std::fmt;
33
34    use windows::Win32::System::EventLog::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS, EVENTLOG_WARNING_TYPE, REPORT_EVENT_TYPE};
35
36
37    #[allow(nonstandard_style)]
38    #[repr(i32)]
39    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
40    pub enum Priority
41    {
42         /// system is unusable
43        LOG_EMERG = 0,
44
45        /// action must be taken immediately
46        LOG_ALERT = 1,
47
48        /// critical conditions
49        LOG_CRIT =  2,
50
51        /// error conditions
52        LOG_ERR =  3,
53
54        /// warning conditions
55        LOG_WARNING = 4,
56
57        /// normal, but significant, condition
58        LOG_NOTICE = 5,
59        
60        /// informational message
61        LOG_INFO = 6,
62
63        /// debug-level message
64        LOG_DEBUG = 7,
65    }
66  
67    impl fmt::Display for Priority
68    {
69        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
70        {
71            //let pri = self.bits & LogMask::LOG_PRIMASK;
72
73            match self
74            {
75                Self::LOG_EMERG => 
76                    write!(f, "[EMERG]"),
77                Self::LOG_ALERT => 
78                    write!(f, "[ALERT]"),
79                Self::LOG_CRIT => 
80                    write!(f, "[CRIT]"),
81                Self::LOG_ERR => 
82                    write!(f, "[ERR]"),
83                Self::LOG_WARNING => 
84                    write!(f, "[WARNING]"),
85                Self::LOG_NOTICE => 
86                    write!(f, "[NOTICE]"),
87                Self::LOG_INFO => 
88                    write!(f, "[INFO]"),
89                Self::LOG_DEBUG => 
90                    write!(f, "[DEBUG]"),
91            }
92        }
93    }
94
95    impl From<Priority> for REPORT_EVENT_TYPE 
96    {
97        fn from(value: Priority) -> Self 
98        {
99            match value
100            {
101                Priority::LOG_EMERG | Priority::LOG_ALERT | Priority::LOG_CRIT =>
102                    return EVENTLOG_WARNING_TYPE,
103                Priority::LOG_ERR => 
104                    return EVENTLOG_ERROR_TYPE,
105                Priority::LOG_WARNING | Priority::LOG_NOTICE => 
106                    return EVENTLOG_INFORMATION_TYPE,
107                Priority::LOG_INFO | Priority::LOG_DEBUG => 
108                    return EVENTLOG_SUCCESS
109            }
110        }
111    }
112
113    impl Priority
114    {
115        /*
116        /// This function validates the `pri` for the incorrects bits set.
117        /// If bits are set incorrectly, resets the invalid bits with:
118        /// *pri & (LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK).
119        ///
120        /// # Arguments
121        ///
122        /// * `pri` - a priority bits
123        ///
124        /// # Returns
125        /// 
126        /// * A [SyRes]. Ok() when valid or Err with error message
127        pub(crate) 
128        fn check_invalid_bits(&mut self) -> SyRes<()>
129        {
130        
131            if (self.bits() & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
132            {
133                let pri_old = self.clone();
134                
135                *self = Self::from_bits_retain(self.bits() & (LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK).bits() );
136
137                throw_error!("unknwon facility/priority: {:x}", pri_old);
138            }
139
140            return Ok(());
141        }*/
142    }
143
144    
145
146    bitflags! {
147        /// Controls  the  operation  of openlog() and subsequent calls to syslog.
148        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
149        pub struct LogStat: i32 
150        {
151            /// Log the process ID with each message. (!todo)
152            const LOG_PID = 1;
153            
154            /// Write directly to the system console if there is an error 
155            /// while sending to the system logger.
156            const LOG_CONS = 2;
157
158            /// The converse of LOG_NDELAY; opening of the connection is delayed 
159            /// until syslog() is called. (This is the default behaviour,and need 
160            /// not be specified.)
161            const LOG_ODELAY = 0;
162
163            /// Open the connection immediately
164            const LOG_NDELAY = 0;
165
166            /// Don't wait for child processes that may have been created 
167            /// while logging the message
168            const LOG_NOWAIT = 0;
169            
170            /// Also log the message to stderr
171            const LOG_PERROR = 0x20;
172        }
173    }
174
175    #[cfg(feature = "build_sync")]
176    impl LogStat
177    {
178        #[inline]
179        pub(crate)
180        fn send_to_stderr(&self, msg: &str)
181        {
182            if self.intersects(LogStat::LOG_PERROR) == true
183            {
184                eprintln!("{}", msg);
185            }
186        }
187
188        #[inline]
189        pub(crate)
190        fn send_to_syscons(&self, msg_payload: &str)
191        {
192            if self.intersects(LogStat::LOG_CONS)
193            {
194                eprintln!("{}", msg_payload);
195            }
196        }
197    }
198
199    bitflags! {
200        /// The facility argument is used to specify what type of program 
201        /// is logging the message.
202        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
203        pub struct LogFacility: i32 
204        {
205            /// kernel messages (these can't be generated from user processes)
206            const LOG_KERN = 0;
207
208            /// (default) generic user-level messages
209            const LOG_USER = 8;
210
211            /// mail subsystem
212            const LOG_MAIL = 16;
213
214            /// system daemons without separate facility value
215            const LOG_DAEMON = 24;
216            
217            /// security/authorization messages
218            const LOG_AUTH = 32;
219
220            /// messages generated internally by syslogd(8)
221            const LOG_SYSLOG = 40;
222
223            /// line printer subsystem
224            const LOG_LPR = 48;
225
226            /// USENET news subsystem
227            const LOG_NEWS = 56;
228
229            /// UUCP subsystem
230            const LOG_UUCP = 64;
231
232            /// reserved for local use
233            const LOG_LOCAL0 = 128;
234
235            /// reserved for local use
236            const LOG_LOCAL1 = 136;
237
238            /// reserved for local use
239            const LOG_LOCAL2 = 144;
240            
241            /// reserved for local use
242            const LOG_LOCAL3 = 152;
243
244            /// reserved for local use
245            const LOG_LOCAL4 = 160;
246
247            /// reserved for local use
248            const LOG_LOCAL5 = 168;
249
250            /// reserved for local use
251            const LOG_LOCAL6 = 176;
252            
253            /// reserved for local use
254            const LOG_LOCAL7 = 184;
255        }
256    }
257
258    impl LogFacility
259    {
260        pub 
261        fn into_win_facility(self) -> u32
262        {
263            return self.bits() as u32 >> 3;
264        }
265    }
266
267    #[cfg(test)]
268    mod tests
269    {
270        use windows::Win32::System::EventLog::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS, EVENTLOG_WARNING_TYPE, REPORT_EVENT_TYPE};
271
272        use crate::Priority;
273
274        #[test]
275        fn test_conversion_prio_to_ret()
276        {
277            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_EMERG), EVENTLOG_WARNING_TYPE);
278            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_ALERT), EVENTLOG_WARNING_TYPE);
279            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_CRIT), EVENTLOG_WARNING_TYPE);
280            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_ERR), EVENTLOG_ERROR_TYPE);
281            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_WARNING), EVENTLOG_INFORMATION_TYPE);
282            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_NOTICE), EVENTLOG_INFORMATION_TYPE);
283            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_INFO), EVENTLOG_SUCCESS);
284            assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_DEBUG), EVENTLOG_SUCCESS);
285        }
286    }
287}
288
289#[cfg(target_family = "unix")]
290pub mod common_syslog_items
291{
292    use nix::libc;
293
294    use std::fmt;
295
296
297    bitflags! {
298        /// Controls  the  operation  of openlog() and subsequent calls to syslog.
299        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
300        pub struct LogStat: libc::c_int 
301        {
302            /// Log the process ID with each message. (!todo)
303            const LOG_PID = libc::LOG_PID;
304            
305            /// Write directly to the system console if there is an error 
306            /// while sending to the system logger.
307            const LOG_CONS = libc::LOG_CONS;
308
309            /// The converse of LOG_NDELAY; opening of the connection is delayed 
310            /// until syslog() is called. (This is the default behaviour,and need 
311            /// not be specified.)
312            const LOG_ODELAY = libc::LOG_ODELAY;
313
314            /// Open the connection immediately
315            const LOG_NDELAY = libc::LOG_NDELAY;
316
317            /// Don't wait for child processes that may have been created 
318            /// while logging the message
319            const LOG_NOWAIT = libc::LOG_NOWAIT;
320            
321            /// Also log the message to stderr
322            const LOG_PERROR = 0x20;
323        }
324    }
325
326    #[cfg(feature = "build_sync")]
327    impl LogStat
328    {
329        #[inline]
330        pub(crate)
331        fn send_to_stderr(&self, msg: &str)
332        {
333            if self.intersects(LogStat::LOG_PERROR) == true
334            {
335                let stderr_lock = std::io::stderr().lock();
336                let newline = "\n";
337
338                let _ = send_to_fd(stderr_lock, msg, &newline);
339            }
340        }
341
342        #[inline]
343        pub(crate)
344        fn send_to_syscons(&self, msg_payload: &str)
345        {
346            use std::fs::File;
347            use std::os::unix::fs::OpenOptionsExt;
348
349            if self.intersects(LogStat::LOG_CONS)
350            {
351                use crate::PATH_CONSOLE;
352
353                let syscons = 
354                    File
355                        ::options()
356                            .create(false)
357                            .read(false)
358                            .write(true)
359                            .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
360                            .open(*PATH_CONSOLE);
361
362                if let Ok(file) = syscons
363                {
364                    let newline = "\n";
365                    let _ = send_to_fd(file, msg_payload, newline);
366                }
367            }
368        }
369    }
370
371    #[allow(nonstandard_style)]
372    #[repr(i32)]
373    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
374    pub enum Priority
375    {
376         /// system is unusable
377        LOG_EMERG = libc::LOG_EMERG,
378
379        /// action must be taken immediately
380        LOG_ALERT = libc::LOG_ALERT,
381
382        /// critical conditions
383        LOG_CRIT =  libc::LOG_CRIT,
384
385        /// error conditions
386        LOG_ERR =  libc::LOG_ERR,
387
388        /// warning conditions
389        LOG_WARNING = libc::LOG_WARNING,
390
391        /// normal, but significant, condition
392        LOG_NOTICE = libc::LOG_NOTICE,
393        
394        /// informational message
395        LOG_INFO = libc::LOG_INFO,
396
397        /// debug-level message
398        LOG_DEBUG = libc::LOG_DEBUG,
399    }
400  
401    impl fmt::Display for Priority
402    {
403        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
404        {
405            //let pri = self.bits & LogMask::LOG_PRIMASK;
406
407            match self
408            {
409                Self::LOG_EMERG => 
410                    write!(f, "[EMERG]"),
411                Self::LOG_ALERT => 
412                    write!(f, "[ALERT]"),
413                Self::LOG_CRIT => 
414                    write!(f, "[CRIT]"),
415                Self::LOG_ERR => 
416                    write!(f, "[ERR]"),
417                Self::LOG_WARNING => 
418                    write!(f, "[WARNING]"),
419                Self::LOG_NOTICE => 
420                    write!(f, "[NOTICE]"),
421                Self::LOG_INFO => 
422                    write!(f, "[INFO]"),
423                Self::LOG_DEBUG => 
424                    write!(f, "[DEBUG]"),
425            }
426        }
427    }
428
429
430    impl Priority
431    {
432    
433    }
434
435    bitflags! {
436        /// The facility argument is used to specify what type of program 
437        /// is logging the message.
438        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
439        pub struct LogFacility: libc::c_int 
440        {
441            /// kernel messages (these can't be generated from user processes)
442            const LOG_KERN = libc::LOG_KERN;
443
444            /// (default) generic user-level messages
445            const LOG_USER = libc::LOG_USER;
446
447            /// mail subsystem
448            const LOG_MAIL = libc::LOG_MAIL;
449
450            /// system daemons without separate facility value
451            const LOG_DAEMON = libc::LOG_DAEMON;
452            
453            /// security/authorization messages
454            const LOG_AUTH = libc::LOG_AUTH;
455
456            /// messages generated internally by syslogd(8)
457            const LOG_SYSLOG = libc::LOG_SYSLOG;
458
459            /// line printer subsystem
460            const LOG_LPR = libc::LOG_LPR;
461
462            /// USENET news subsystem
463            const LOG_NEWS = libc::LOG_NEWS;
464
465            /// UUCP subsystem
466            const LOG_UUCP = libc::LOG_UUCP;
467
468            /// reserved for local use
469            const LOG_LOCAL0 = libc::LOG_LOCAL0;
470
471            /// reserved for local use
472            const LOG_LOCAL1 = libc::LOG_LOCAL1;
473
474            /// reserved for local use
475            const LOG_LOCAL2 = libc::LOG_LOCAL2;
476            
477            /// reserved for local use
478            const LOG_LOCAL3 = libc::LOG_LOCAL3;
479
480            /// reserved for local use
481            const LOG_LOCAL4 = libc::LOG_LOCAL4;
482
483            /// reserved for local use
484            const LOG_LOCAL5 = libc::LOG_LOCAL5;
485
486            /// reserved for local use
487            const LOG_LOCAL6 = libc::LOG_LOCAL6;
488            
489            /// reserved for local use
490            const LOG_LOCAL7 = libc::LOG_LOCAL7;
491        }
492    }
493
494
495    
496
497    /// Unpriv socket
498    pub const PATH_LOG: &'static str = "/var/run/log";
499
500    /// Priviledged socket
501    pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
502
503    /// backward compatibility
504    pub const PATH_OLDLOG: &'static str = "/dev/log";
505
506    /// OSX compat
507    pub const PATH_OSX: &'static str = "/var/run/syslog";
508
509    /*
510    pub static PATH_CONSOLE: LazyLock<CString> = LazyLock::new(|| 
511        {
512            CString::new("/dev/console").unwrap()
513        }
514    );
515    */
516
517    #[cfg(feature = "build_sync")]
518    pub(crate) mod sync_portion
519    {
520        use std::io::Write;
521        use std::io::IoSlice;
522        use crate::error::SyRes;
523        use crate::map_error_os;
524
525        /// Sends to the FD i.e file of stderr, stdout or any which 
526        /// implements [Write] `write_vectored`.
527        ///
528        /// # Arguments
529        /// 
530        /// * `file_fd` - mutable consume of the container FD.
531        ///
532        /// * `msg` - a reference on array of data
533        ///
534        /// * `newline` - a new line string ref i.e "\n" or "\r\n"
535        pub(crate) 
536        fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
537        where W: Write
538        {
539            return 
540                file_fd
541                    .write_vectored(
542                        &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
543                    )
544                    .map_err(|e|
545                        map_error_os!(e, "send_to_fd() writev() failed")
546                    );
547        }
548    }
549
550    #[cfg(feature = "build_sync")]
551    pub(crate) use self::sync_portion::*;
552
553    #[cfg(test)]
554    mod tests
555    {
556        use super::*;
557
558        #[cfg(feature = "build_sync")]
559        #[test]
560        fn test_error_message()
561        {
562            /*use std::sync::Arc;
563            use std::thread;
564            use std::time::Duration;
565            use super::{LOG_MASK};*/
566
567            let testmsg = "this is test message!";
568            let newline = "\n";
569            let stderr_lock = std::io::stderr().lock();
570            let res = send_to_fd(stderr_lock, testmsg, &newline);
571
572            println!("res: {:?}", res);
573
574            assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
575        
576            return;
577        }
578
579        #[test]
580        fn test_priority_shl()
581        {
582            assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
583        }
584    }
585}
586
587/// Path to console.
588pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(|| 
589    {
590        Path::new("/dev/console")
591    }
592);
593
594/// A max dgram init.
595pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(|| 
596    {
597        portable::get_local_dgram_maxdgram() as usize
598    }
599);
600
601
602/// max hostname size
603pub const MAXHOSTNAMELEN: usize = 256;
604
605/// mask to extract facility part
606pub const LOG_FACMASK: i32 = 0x03f8;
607
608/// Maximum number of characters of syslog message.
609/// According to RFC5424. However syslog-protocol also may state that 
610/// the max message will be defined by the transport layer.
611pub const MAXLINE: usize = 8192;
612
613/// RFC3164 limit
614pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
615
616/// A maximum message which could be passed to Windows Event Log
617pub const WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN: usize = 31839;
618
619#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
620compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
621
622// RFC5424 480 octets or limited by the (transport) MAX_DGRAM_LEN or other.
623#[cfg(feature = "udp_truncate_1024_bytes")]
624pub const RFC5424_UDP_MAX_PKT_LEN: usize  = 1024;
625
626#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
627pub const RFC5424_UDP_MAX_PKT_LEN: usize  = 2048;
628
629#[cfg(feature = "tcp_truncate_1024_bytes")]
630pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 1024;
631
632#[cfg(feature = "tcp_truncate_2048_bytes")]
633pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 2048;
634
635#[cfg(feature = "tcp_truncate_4096_bytes")]
636pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 4096;
637
638#[cfg(feature = "tcp_truncate_max_bytes")]
639pub const RFC5424_TCP_MAX_PKT_LEN: usize  = MAXLINE;
640
641/// A max byte lenth of APPNAME (NILVALUE / 1*48PRINTUSASCII)
642pub const RFC_MAX_APP_NAME: usize = 48;
643
644/// A private enterprise number.
645pub const IANA_PRIV_ENT_NUM: u64 = 32473;
646
647/// RFC5424 defined value.
648pub const NILVALUE: &'static str = "-";
649
650/// RFC5424 escape character.
651pub const ESC_CHAR_REPL: &'static str = "#000";
652
653/// RFC5424 defined value (bytes).
654pub const NILVALUE_B: &'static [u8] = b"-";
655
656/// White space
657pub const WSPACE: &'static str = " ";
658
659/// Opening brace ('[', ABNF )
660pub const OBRACE: &'static str = "[";
661
662/// Closing brace (']', ABNF %d93)
663pub const CBRACE: &'static str = "]";
664
665/// Closing brace RFC3...
666pub const CBRACE_SEM: &'static str = "]:";
667
668/// Quote-character ('"', ABNF %d34)
669pub const QCHAR: &'static str = "\"";
670
671/// At-sign ("@", ABNF %d64)
672pub const ATSIGN: &'static str = "@";
673
674/// Eq-sign ("=", ABNF %d61)
675pub const EQSIGN: &'static str = "=";
676
677/// A cursor return.
678pub const NEXTLINE: &'static str = "\n";
679
680bitflags! {
681    /// Masks
682    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
683    pub(crate) struct LogMask: i32 
684    {
685        /// A mask for the [LogFacility]
686        const LOG_FACMASK = 0x3f8;
687
688        /// A mask for the [Priority]
689        const LOG_PRIMASK = 7;
690    }
691}
692
693/// A struct which contains syslog encoded Facility and Priority 
694/// is the following order:
695/// 
696/// - log_facility mask 0x3f8
697/// - priority mask 0x7
698#[derive(Debug, Clone, Copy, PartialEq, Eq)]
699pub struct SyslogMsgPriFac(i32);
700
701impl fmt::Display for SyslogMsgPriFac
702{
703    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
704    {
705        write!(f, "{}", self.0)
706    }
707}
708
709impl TryFrom<i32> for SyslogMsgPriFac
710{
711    type Error = SyslogError;
712
713    fn try_from(value: i32) -> Result<Self, Self::Error> 
714    {
715        if (value & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
716        {
717            throw_error!("unknwon facility/priority: {:x}", value);
718        }
719
720        return Ok(Self(value));
721    }
722}
723
724impl TryFrom<&[u8]> for SyslogMsgPriFac
725{
726    type Error = SyslogError;
727
728    fn try_from(value: &[u8]) -> Result<Self, Self::Error> 
729    {
730        let val = 
731            str::from_utf8(value)
732                .map_or(
733                    None, 
734                    |pri_str|
735                    {
736                        i32::from_str_radix(pri_str, 10)
737                            .map_or(
738                                None,
739                                |val|
740                                Some(val & LogMask::LOG_PRIMASK | val & LogMask::LOG_FACMASK.bits()) 
741                            )
742                    }
743                )
744                .ok_or(
745                    map_error_code!(InternalError, "cannot convert to prio|facility")
746                )?;
747
748        if (val & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
749        {
750            throw_error!("unknwon facility/priority: {:x}", val);
751        }
752
753        return Ok(Self(val));
754    }
755}
756
757impl SyslogMsgPriFac
758{
759    /// Initializes the instance by adding a priority and facility together
760    /// to form a message header.
761    pub(crate) 
762    fn set_facility(p: Priority, f: LogFacility) -> Self
763    {
764        return Self( p as i32 | f.bits() );
765    }
766
767    /// Returns the raw value.
768    pub 
769    fn get_val(&self) -> i32
770    {
771        return self.0;
772    }
773
774    /// Reads from the inner a [Priority].
775    pub 
776    fn get_priority(&self) -> Priority
777    {
778        return Priority::from(self.0 & LogMask::LOG_PRIMASK);
779    }
780
781    /// Reads from inner a [LogFacility].
782    pub 
783    fn get_log_facility(&self) -> LogFacility
784    {
785        return LogFacility::from_bits_retain(self.0 & LogMask::LOG_FACMASK);
786    }
787}
788
789impl From<i32> for Priority
790{
791    fn from(value: i32) -> Self 
792    {
793        if value == Priority::LOG_ALERT as i32
794        {
795            return Priority::LOG_ALERT;
796        }
797        else if value == Priority::LOG_CRIT as i32
798        {
799            return Priority::LOG_CRIT;
800        }
801        else if value == Priority::LOG_DEBUG as i32
802        {
803            return Priority::LOG_DEBUG;
804        }
805        else if value == Priority::LOG_EMERG as i32
806        {
807            return Priority::LOG_EMERG;
808        }
809        else if value == Priority::LOG_ERR as i32
810        {
811            return Priority::LOG_ERR;
812        }
813        else if value == Priority::LOG_INFO as i32
814        {
815            return Priority::LOG_INFO;
816        }
817        else if value == Priority::LOG_NOTICE as i32
818        {
819            return Priority::LOG_NOTICE;
820        }
821        else
822        {
823            return Priority::LOG_WARNING;
824        }
825    }
826}
827
828/// LOG_MASK is used to create the priority mask in setlogmask. 
829/// For a single Priority mask
830/// used with [Priority]
831/// can be used with | & ! bit operations LOG_MASK()
832///
833/// # Examples
834/// 
835/// ```ignore
836///     LOG_MASK!(Priority::LOG_ALERT) | LOG_MASK!(Priority::LOG_INFO)
837/// ```
838#[macro_export]
839macro_rules! LOG_MASK 
840{
841    ($arg:expr) => (
842        (1 << ($arg))
843    )
844}
845
846/// LOG_MASK is used to create the priority mask in setlogmask
847/// For a mask UPTO specified
848/// used with [Priority]
849///
850/// # Examples
851/// 
852/// ```ignore
853///     LOG_UPTO!(Priority::LOG_ALERT)
854/// ```
855#[macro_export]
856macro_rules! LOG_UPTO 
857{
858    ($arg:expr) => (
859        ((1 << (($arg) + 1)) - 1)
860    )
861}
862
863impl Shl<Priority> for i32
864{
865    type Output = i32;
866
867    fn shl(self, rhs: Priority) -> i32 
868    {
869        let lhs = self;
870        return lhs << rhs as i32;
871    }
872}
873
874impl BitAnd<Priority> for i32
875{
876    type Output = i32;
877
878    #[inline]
879    fn bitand(self, rhs: Priority) -> i32
880    {
881        return self & rhs as i32;
882    }
883}
884
885impl BitAnd<LogMask> for Priority
886{
887    type Output = i32;
888
889    #[inline]
890    fn bitand(self, rhs: LogMask) -> i32
891    {
892        return self as i32 & rhs.bits();
893    }
894}
895
896impl BitAnd<LogMask> for LogFacility 
897{
898    type Output = LogFacility;
899
900    #[inline]
901    fn bitand(self, rhs: LogMask) -> Self::Output
902    {
903        return Self::from_bits_retain(self.bits() & rhs.bits());
904    }
905}
906
907impl BitAnd<LogMask> for i32 
908{
909    type Output = i32;
910
911    #[inline]
912    fn bitand(self, rhs: LogMask) -> i32
913    {
914        return self & rhs.bits();
915    }
916}
917
918/// This function trancated 1 last UTF8 character from the string.
919///
920/// # Arguments
921///
922/// * `lt` - a string which is trucated
923/// 
924/// # Returns
925/// 
926/// * A reference to the ctruncated string
927pub 
928fn truncate(lt: &str) -> &str
929{
930    let ltt =
931        match lt.char_indices().nth(lt.len()-1) 
932        {
933            None => lt,
934            Some((idx, _)) => &lt[..idx],
935        };
936    return ltt;
937}
938
939/// Trancates the string up to closest to N byte equiv UTF8
940///  if string exceeds size
941/// 
942/// For example:  
943/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=3  
944/// will give 'ボ'  
945/// 
946/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=4  
947/// will give 'ボ' 
948/// 
949/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=1  
950/// will give ''
951/// 
952/// # Arguments
953///
954/// * `lt` - a string to truncate
955///
956/// * `n` - a size (in bytes, not in chars)
957/// 
958/// # Returns 
959///
960/// * A reference to [str] with the time `'t` which corresponds to
961/// the lifetile of the input argument `'t`.
962pub 
963fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
964{
965    if lt.as_bytes().len() <= n
966    {
967        return lt;
968    }
969
970    let mut nn: usize = 0;
971    let mut cc = lt.chars();
972    let mut ln: usize;
973
974    loop 
975    {
976        match cc.next()
977        {
978            Some(r) =>
979            {
980                ln = r.len_utf8();
981                nn += ln;
982
983                if nn == n
984                {
985                    return &lt[..nn];
986                }
987                else if nn > n
988                {
989                    return &lt[..nn-ln];
990                }
991            },
992            None => 
993                return lt,
994        }
995    }
996}
997
998/// Checks if string are:
999/// ```text
1000/// NOT EMPTY
1001/// MUST be printable US-ASCII strings, and MUST
1002/// NOT contain an at-sign ('@', ABNF %d64), an equal-sign ('=', ABNF
1003/// %d61), a closing brace (']', ABNF %d93), a quote-character ('"',
1004/// ABNF %d34), whitespace, or control characters
1005/// ```
1006pub
1007fn check_printable(a: &str) -> SyRes<()>
1008{
1009    if a.is_empty() == true
1010    {
1011        throw_error!("empty SD value");
1012    }
1013
1014    for p in a.chars()
1015    {
1016        if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
1017        {
1018            throw_error!("incorrect char: '{:X}' in SD value", p as u64);
1019        }
1020    }
1021
1022    return Ok(());
1023}
1024
1025
1026pub 
1027fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
1028{
1029    let mut out = String::with_capacity(st.len());
1030
1031    for c in st.chars()
1032    {
1033        if c.is_control() == true
1034        {
1035            out.push_str(ESC_CHAR_REPL);
1036        }
1037        else if c == '\"' || c == '\\' || c == ']'
1038        {
1039            out.push('\\');
1040            out.push(c);
1041        }
1042        else
1043        {
1044            out.push(c);
1045        }
1046    }
1047
1048    if st.len() == out.len()
1049    {
1050        return st;
1051    }
1052    else
1053    {
1054        return Cow::Owned(out);
1055    }
1056}
1057
1058
1059#[cfg(test)]
1060mod tests
1061{
1062    use super::*;
1063
1064    #[test]
1065    fn test_truncate()
1066    {
1067        let test = "cat\n";
1068
1069        let trunc = truncate(test);
1070
1071        assert_eq!("cat", trunc);
1072    }
1073
1074
1075
1076    #[test]
1077    fn test_truncate_n()
1078    {
1079        assert_eq!(truncate_n("abcde", 3), "abc");
1080        assert_eq!(truncate_n("ボルテ", 4), "ボ");
1081        assert_eq!(truncate_n("ボルテ", 5), "ボ");
1082        assert_eq!(truncate_n("ボルテ", 6), "ボル");
1083        assert_eq!(truncate_n("abcde", 0), "");
1084        assert_eq!(truncate_n("abcde", 5), "abcde");
1085        assert_eq!(truncate_n("abcde", 6), "abcde");
1086        assert_eq!(truncate_n("ДАТА", 3), "Д");
1087        assert_eq!(truncate_n("ДАТА", 4), "ДА");
1088        assert_eq!(truncate_n("ДАТА", 1), "");
1089    }
1090
1091}