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