Skip to main content

apple_log/
os_signposter.rs

1#![allow(clippy::missing_panics_doc, clippy::should_implement_trait)]
2
3use core::ffi::c_void;
4use std::ptr::NonNull;
5
6use crate::bridge_support::{bridge_ptr_result, c_string_arg, sanitized_c_string};
7use crate::error::LogError;
8use crate::ffi;
9use crate::logger::Logger;
10use crate::os_log::OSLog;
11use crate::os_signpost_id::OSSignpostId;
12
13/// A started signpost interval.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct OSSignpostInterval {
16    id: OSSignpostId,
17    animation: bool,
18}
19
20impl OSSignpostInterval {
21    #[must_use]
22    pub const fn id(self) -> OSSignpostId {
23        self.id
24    }
25
26    #[must_use]
27    pub const fn is_animation(self) -> bool {
28        self.animation
29    }
30}
31
32/// Signpost emitter backed by an `OSLog` handle.
33pub struct OSSignposter {
34    ptr: NonNull<c_void>,
35}
36
37impl OSSignposter {
38    fn bridge_default() -> Self {
39        Self {
40            ptr: NonNull::new(unsafe { ffi::apple_log_os_signposter_default() })
41                .expect("Swift bridge never returns NULL for OSSignposter.default"),
42        }
43    }
44
45    /// Creates a signposter for a subsystem/category pair.
46    ///
47    /// # Errors
48    ///
49    /// Returns an error if either string contains a NUL byte or the bridge fails.
50    pub fn new(subsystem: &str, category: &str) -> Result<Self, LogError> {
51        let subsystem = c_string_arg("subsystem", subsystem)?;
52        let category = c_string_arg("category", category)?;
53        let ptr = bridge_ptr_result("OSSignposter::new", |error_out| unsafe {
54            ffi::apple_log_os_signposter_create(subsystem.as_ptr(), category.as_ptr(), error_out)
55        })?;
56        Ok(Self { ptr })
57    }
58
59    /// Creates a signposter from an `OSLog` handle.
60    ///
61    /// # Errors
62    ///
63    /// Returns an error if the bridge fails.
64    pub fn from_os_log(log: &OSLog) -> Result<Self, LogError> {
65        let ptr = bridge_ptr_result("OSSignposter::from_os_log", |error_out| unsafe {
66            ffi::apple_log_os_signposter_from_os_log(log.as_ptr(), error_out)
67        })?;
68        Ok(Self { ptr })
69    }
70
71    /// Creates a signposter from a `Logger`.
72    ///
73    /// # Errors
74    ///
75    /// Returns an error if the bridge fails.
76    pub fn from_logger(logger: &Logger) -> Result<Self, LogError> {
77        let ptr = bridge_ptr_result("OSSignposter::from_logger", |error_out| unsafe {
78            ffi::apple_log_os_signposter_from_logger(logger.as_ptr(), error_out)
79        })?;
80        Ok(Self { ptr })
81    }
82
83    #[must_use]
84    pub fn default() -> Self {
85        Self::bridge_default()
86    }
87
88    #[must_use]
89    pub fn disabled() -> Self {
90        Self {
91            ptr: NonNull::new(unsafe { ffi::apple_log_os_signposter_disabled() })
92                .expect("Swift bridge never returns NULL for OSSignposter.disabled"),
93        }
94    }
95
96    #[must_use]
97    pub fn is_enabled(&self) -> bool {
98        unsafe { ffi::apple_log_os_signposter_is_enabled(self.ptr.as_ptr()) }
99    }
100
101    #[must_use]
102    pub fn make_signpost_id(&self) -> OSSignpostId {
103        OSSignpostId::from_u64(unsafe { ffi::apple_log_os_signposter_make_signpost_id(self.ptr.as_ptr()) })
104    }
105
106    #[must_use]
107    pub fn make_signpost_id_from_pointer<T>(&self, pointer: *const T) -> OSSignpostId {
108        OSSignpostId::from_u64(unsafe {
109            ffi::apple_log_os_signposter_make_signpost_id_from_pointer(self.ptr.as_ptr(), pointer.cast())
110        })
111    }
112
113    pub fn emit_event(&self, name: &str, id: OSSignpostId, message: &str) {
114        let name = sanitized_c_string(name);
115        let message = sanitized_c_string(message);
116        unsafe {
117            ffi::apple_log_os_signposter_emit_event(
118                self.ptr.as_ptr(),
119                id.as_u64(),
120                name.as_ptr(),
121                message.as_ptr(),
122            );
123        }
124    }
125
126    #[must_use]
127    pub fn begin_interval(&self, name: &str, id: OSSignpostId, message: &str) -> OSSignpostInterval {
128        let name = sanitized_c_string(name);
129        let message = sanitized_c_string(message);
130        unsafe {
131            ffi::apple_log_os_signposter_begin_interval(
132                self.ptr.as_ptr(),
133                id.as_u64(),
134                name.as_ptr(),
135                message.as_ptr(),
136            );
137        }
138        OSSignpostInterval { id, animation: false }
139    }
140
141    #[must_use]
142    pub fn begin_animation_interval(
143        &self,
144        name: &str,
145        id: OSSignpostId,
146        message: &str,
147    ) -> OSSignpostInterval {
148        let name = sanitized_c_string(name);
149        let message = sanitized_c_string(message);
150        unsafe {
151            ffi::apple_log_os_signposter_begin_animation_interval(
152                self.ptr.as_ptr(),
153                id.as_u64(),
154                name.as_ptr(),
155                message.as_ptr(),
156            );
157        }
158        OSSignpostInterval { id, animation: true }
159    }
160
161    pub fn end_interval(&self, name: &str, interval: OSSignpostInterval, message: &str) {
162        let name = sanitized_c_string(name);
163        let message = sanitized_c_string(message);
164        unsafe {
165            ffi::apple_log_os_signposter_end_interval(
166                self.ptr.as_ptr(),
167                interval.id.as_u64(),
168                name.as_ptr(),
169                message.as_ptr(),
170            );
171        }
172    }
173
174    pub fn with_interval_signpost<T>(
175        &self,
176        name: &str,
177        id: OSSignpostId,
178        message: &str,
179        around: impl FnOnce() -> T,
180    ) -> T {
181        let interval = self.begin_interval(name, id, message);
182        let result = around();
183        self.end_interval(name, interval, message);
184        result
185    }
186}
187
188impl Default for OSSignposter {
189    fn default() -> Self {
190        Self::bridge_default()
191    }
192}
193
194impl Drop for OSSignposter {
195    fn drop(&mut self) {
196        unsafe { ffi::apple_log_os_signposter_release(self.ptr.as_ptr()) };
197    }
198}