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