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