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 {
104            ffi::apple_log_os_signposter_make_signpost_id(self.ptr.as_ptr())
105        })
106    }
107
108    #[must_use]
109    pub fn make_signpost_id_from_pointer<T>(&self, pointer: *const T) -> OSSignpostId {
110        OSSignpostId::from_u64(unsafe {
111            ffi::apple_log_os_signposter_make_signpost_id_from_pointer(
112                self.ptr.as_ptr(),
113                pointer.cast(),
114            )
115        })
116    }
117
118    pub fn emit_event(&self, name: &str, id: OSSignpostId, message: &str) {
119        let name = sanitized_c_string(name);
120        let message = sanitized_c_string(message);
121        unsafe {
122            ffi::apple_log_os_signposter_emit_event(
123                self.ptr.as_ptr(),
124                id.as_u64(),
125                name.as_ptr(),
126                message.as_ptr(),
127            );
128        }
129    }
130
131    #[must_use]
132    pub fn begin_interval(
133        &self,
134        name: &str,
135        id: OSSignpostId,
136        message: &str,
137    ) -> OSSignpostInterval {
138        let name = sanitized_c_string(name);
139        let message = sanitized_c_string(message);
140        unsafe {
141            ffi::apple_log_os_signposter_begin_interval(
142                self.ptr.as_ptr(),
143                id.as_u64(),
144                name.as_ptr(),
145                message.as_ptr(),
146            );
147        }
148        OSSignpostInterval {
149            id,
150            animation: false,
151        }
152    }
153
154    #[must_use]
155    pub fn begin_animation_interval(
156        &self,
157        name: &str,
158        id: OSSignpostId,
159        message: &str,
160    ) -> OSSignpostInterval {
161        let name = sanitized_c_string(name);
162        let message = sanitized_c_string(message);
163        unsafe {
164            ffi::apple_log_os_signposter_begin_animation_interval(
165                self.ptr.as_ptr(),
166                id.as_u64(),
167                name.as_ptr(),
168                message.as_ptr(),
169            );
170        }
171        OSSignpostInterval {
172            id,
173            animation: true,
174        }
175    }
176
177    pub fn end_interval(&self, name: &str, interval: OSSignpostInterval, message: &str) {
178        let name = sanitized_c_string(name);
179        let message = sanitized_c_string(message);
180        unsafe {
181            ffi::apple_log_os_signposter_end_interval(
182                self.ptr.as_ptr(),
183                interval.id.as_u64(),
184                name.as_ptr(),
185                message.as_ptr(),
186            );
187        }
188    }
189
190    pub fn with_interval_signpost<T>(
191        &self,
192        name: &str,
193        id: OSSignpostId,
194        message: &str,
195        around: impl FnOnce() -> T,
196    ) -> T {
197        let interval = self.begin_interval(name, id, message);
198        let result = around();
199        self.end_interval(name, interval, message);
200        result
201    }
202}
203
204impl Default for OSSignposter {
205    fn default() -> Self {
206        Self::bridge_default()
207    }
208}
209
210impl Drop for OSSignposter {
211    fn drop(&mut self) {
212        unsafe { ffi::apple_log_os_signposter_release(self.ptr.as_ptr()) };
213    }
214}