clap_clap/ext/
log.rs

1use std::{
2    ffi::{CStr, CString, NulError},
3    fmt::{Display, Formatter},
4};
5
6use crate::{
7    ffi::{
8        CLAP_LOG_DEBUG, CLAP_LOG_ERROR, CLAP_LOG_FATAL, CLAP_LOG_HOST_MISBEHAVING, CLAP_LOG_INFO,
9        CLAP_LOG_PLUGIN_MISBEHAVING, CLAP_LOG_WARNING, clap_host_log, clap_log_severity,
10    },
11    host::Host,
12};
13
14#[derive(Debug)]
15pub struct HostLog<'a> {
16    host: &'a Host,
17    clap_host_log: &'a clap_host_log,
18}
19
20impl<'a> HostLog<'a> {
21    /// # Safety
22    ///
23    /// All extension interface function pointers must be non-null (Some), and
24    /// the functions must be thread-safe.
25    pub(crate) const unsafe fn new_unchecked(
26        host: &'a Host,
27        clap_host_log: &'a clap_host_log,
28    ) -> Self {
29        Self {
30            host,
31            clap_host_log,
32        }
33    }
34
35    /// Send a `CStr` to the host's log.  
36    ///
37    /// By logging a `CStr`, instead of `&str`, the function avoids memory
38    /// allocation, and a fallible Rust-string-to-C-string conversion.
39    pub fn log_cstr(&self, severity: Severity, msg: &CStr) {
40        // SAFETY: By construction, the callback must be a valid function pointer,
41        // and the call is thread-safe.
42        let callback = self.clap_host_log.log.unwrap();
43        unsafe { callback(self.host.clap_host(), severity.into(), msg.as_ptr()) }
44    }
45
46    pub fn log(&self, severity: Severity, msg: &str) -> Result<(), Error> {
47        self.log_cstr(severity, &CString::new(msg)?);
48        Ok(())
49    }
50}
51
52macro_rules! impl_log_severity {
53    ($(($method:tt, $severity:ident)),*) => {
54        impl<'a> HostLog<'a> {
55            $(
56                pub fn $method(&self, msg: &str) -> Result<(), Error> {
57                    self.log(Severity::$severity, msg)
58                }
59            )*
60        }
61    };
62}
63
64impl_log_severity!(
65    (debug, Debug),
66    (info, Info),
67    (warning, Warning),
68    (error, Error),
69    (fatal, Fatal)
70);
71
72#[derive(Debug, Copy, Clone, PartialEq)]
73pub enum Severity {
74    Debug,
75    Info,
76    Warning,
77    Error,
78    Fatal,
79    HostMisbehaving,
80    PluginMisbehaving,
81}
82
83impl From<Severity> for clap_log_severity {
84    fn from(value: Severity) -> Self {
85        use Severity::*;
86
87        match value {
88            Debug => CLAP_LOG_DEBUG,
89            Info => CLAP_LOG_INFO,
90            Warning => CLAP_LOG_WARNING,
91            Error => CLAP_LOG_ERROR,
92            Fatal => CLAP_LOG_FATAL,
93            HostMisbehaving => CLAP_LOG_HOST_MISBEHAVING,
94            PluginMisbehaving => CLAP_LOG_PLUGIN_MISBEHAVING,
95        }
96    }
97}
98
99#[derive(Debug, Clone, PartialEq)]
100pub enum Error {
101    NulError(NulError),
102}
103
104impl Display for Error {
105    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106        match self {
107            Error::NulError(e) => write!(f, "error converting to C string: {e}"),
108        }
109    }
110}
111
112impl std::error::Error for Error {}
113
114impl From<NulError> for Error {
115    fn from(value: NulError) -> Self {
116        Self::NulError(value)
117    }
118}
119
120impl From<Error> for crate::Error {
121    fn from(value: Error) -> Self {
122        crate::ext::Error::Log(value).into()
123    }
124}