syslog_client/writer/
mod.rs

1//!Logger writer
2use core::fmt;
3
4use crate::syslog::Severity;
5
6#[cfg(feature = "std")]
7mod std;
8///Builtin transports
9pub mod transport {
10    #[cfg(feature = "std")]
11    pub use super::std::*;
12}
13
14///Transport builder trait
15pub trait MakeTransport {
16    ///Internal Error Type
17    type Error: TransportError;
18    ///Transport type
19    type Transport: Transport<Self::Error>;
20
21    ///Creates instance
22    ///
23    ///This function is called when required or previous instance is no longer able to write
24    fn create(&self) -> Result<Self::Transport, Self::Error>;
25}
26
27///Utility trait to determine write error handling
28pub trait TransportError: fmt::Debug {
29    ///Returns whether write error indicates Transport cannot be used
30    ///
31    ///If `true`, then Transport shall be created anew using `MakeTransport`
32    fn is_terminal(&self) -> bool;
33}
34
35///Log writer
36pub trait Transport<ERR: TransportError> {
37    ///Performs write of the full encoded message.
38    ///
39    ///Severity is encoded already and is only for informational purpose
40    fn write(&mut self, severity: Severity, msg: &str) -> Result<(), ERR>;
41}
42
43impl<IO: MakeTransport> MakeTransport for &'_ IO {
44    type Error = IO::Error;
45    type Transport = IO::Transport;
46
47    #[inline(always)]
48    fn create(&self) -> Result<Self::Transport, Self::Error> {
49        MakeTransport::create(*self)
50    }
51}
52
53pub(crate) struct Writer<IO: MakeTransport> {
54    transport: IO,
55    cached_writer: Option<IO::Transport>,
56}
57
58impl<IO: MakeTransport> Writer<IO> {
59    #[inline(always)]
60    pub(crate) const fn new(transport: IO) -> Self {
61        Self {
62            transport,
63            cached_writer: None,
64        }
65    }
66
67    pub(crate) fn write_buffer(&mut self, buffer: &str, severity: Severity, retry_count: u8) -> Result<(), IO::Error> {
68        //We will try once + retry_count
69        let mut retry_attempts = retry_count.saturating_add(1);
70
71        loop {
72            retry_attempts = retry_attempts.saturating_sub(1);
73
74            let mut writer = match self.cached_writer.take() {
75                Some(writer) => writer,
76                None => match self.transport.create() {
77                    Ok(writer) => writer,
78                    //If interface error indicates you cannot proceed then give up immediately
79                    Err(error) if error.is_terminal() => return Err(error),
80                    Err(_) if retry_attempts > 0 => continue,
81                    Err(error) => break Err(error),
82                },
83            };
84
85            match writer.write(severity, buffer) {
86                Ok(()) => {
87                    //Only cache writer, if it is able to write
88                    self.cached_writer = Some(writer);
89                    break Ok(());
90                }
91                //If interface error indicates you cannot proceed then give up immediately
92                //Also there is high risk in caching interface that errors out, so avoid that
93                Err(error) if error.is_terminal() => continue,
94                Err(_) if retry_attempts > 0 => {
95                    self.cached_writer = Some(writer);
96                },
97                Err(error) => break Err(error),
98            }
99        }
100    }
101}