1#![allow(clippy::missing_panics_doc, clippy::use_self)]
2
3use core::ffi::c_void;
4use core::ops::{BitOr, BitOrAssign};
5use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
6use std::ptr::NonNull;
7
8use crate::bridge_support::{bridge_ptr_result, c_string_arg, sanitized_c_string};
9use crate::error::LogError;
10use crate::ffi;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct ActivityIds {
15 pub current: u64,
16 pub parent: Option<u64>,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
21pub struct OSActivityFlags(u32);
22
23impl OSActivityFlags {
24 pub const DEFAULT: Self = Self(0);
25 pub const DETACHED: Self = Self(0x1);
26 pub const IF_NONE_PRESENT: Self = Self(0x2);
27
28 #[must_use]
29 pub const fn bits(self) -> u32 {
30 self.0
31 }
32}
33
34impl BitOr for OSActivityFlags {
35 type Output = Self;
36
37 fn bitor(self, rhs: Self) -> Self::Output {
38 Self(self.0 | rhs.0)
39 }
40}
41
42impl BitOrAssign for OSActivityFlags {
43 fn bitor_assign(&mut self, rhs: Self) {
44 self.0 |= rhs.0;
45 }
46}
47
48pub struct OSActivityScope {
50 ptr: NonNull<c_void>,
51}
52
53impl Drop for OSActivityScope {
54 fn drop(&mut self) {
55 unsafe { ffi::apple_log_os_activity_scope_leave(self.ptr.as_ptr()) };
56 }
57}
58
59pub struct OSActivity {
61 ptr: NonNull<c_void>,
62}
63
64struct ApplyContext<F> {
65 closure: Option<F>,
66 panic: Option<Box<dyn std::any::Any + Send>>,
67}
68
69unsafe extern "C" fn apply_trampoline<F>(context: *mut c_void)
70where
71 F: FnOnce(),
72{
73 let context = &mut *context.cast::<ApplyContext<F>>();
74 let closure = context
75 .closure
76 .take()
77 .expect("OSActivity apply trampoline called at most once");
78 if let Err(panic) = catch_unwind(AssertUnwindSafe(closure)) {
79 context.panic = Some(panic);
80 }
81}
82
83impl OSActivity {
84 pub fn new(
90 description: &str,
91 parent: Option<&OSActivity>,
92 flags: OSActivityFlags,
93 ) -> Result<Self, LogError> {
94 let description = c_string_arg("description", description)?;
95 let ptr = bridge_ptr_result("OSActivity::new", |error_out| unsafe {
96 ffi::apple_log_os_activity_create(
97 description.as_ptr(),
98 parent.map_or(std::ptr::null_mut(), OSActivity::as_ptr),
99 flags.bits(),
100 error_out,
101 )
102 })?;
103 Ok(Self { ptr })
104 }
105
106 pub fn start(description: &str, flags: OSActivityFlags) -> Result<Self, LogError> {
112 let description = c_string_arg("description", description)?;
113 let ptr = bridge_ptr_result("OSActivity::start", |error_out| unsafe {
114 ffi::apple_log_os_activity_start(description.as_ptr(), flags.bits(), error_out)
115 })?;
116 Ok(Self { ptr })
117 }
118
119 #[must_use]
120 pub fn current() -> Self {
121 Self {
122 ptr: NonNull::new(unsafe { ffi::apple_log_os_activity_current() })
123 .expect("Swift bridge never returns NULL for OSActivity.current"),
124 }
125 }
126
127 #[must_use]
128 pub fn none() -> Self {
129 Self {
130 ptr: NonNull::new(unsafe { ffi::apple_log_os_activity_none() })
131 .expect("Swift bridge never returns NULL for OSActivity.none"),
132 }
133 }
134
135 #[must_use]
136 pub fn identifiers(&self) -> ActivityIds {
137 let mut parent = 0_u64;
138 let current = unsafe { ffi::apple_log_os_activity_get_identifier(self.ptr.as_ptr(), &mut parent) };
139 ActivityIds {
140 current,
141 parent: (parent != 0).then_some(parent),
142 }
143 }
144
145 #[must_use]
146 pub fn identifier(&self) -> u64 {
147 self.identifiers().current
148 }
149
150 pub fn apply<F>(&self, closure: F)
151 where
152 F: FnOnce(),
153 {
154 let mut context = ApplyContext {
155 closure: Some(closure),
156 panic: None,
157 };
158 unsafe {
159 ffi::apple_log_os_activity_apply(
160 self.ptr.as_ptr(),
161 std::ptr::addr_of_mut!(context).cast(),
162 Some(apply_trampoline::<F>),
163 );
164 }
165 if let Some(panic) = context.panic {
166 resume_unwind(panic);
167 }
168 }
169
170 pub fn enter(&self) -> Result<OSActivityScope, LogError> {
176 let ptr = NonNull::new(unsafe { ffi::apple_log_os_activity_scope_enter(self.ptr.as_ptr()) })
177 .ok_or_else(|| LogError::bridge("OSActivity::enter returned NULL"))?;
178 Ok(OSActivityScope { ptr })
179 }
180
181 pub fn end(&self) {
182 unsafe { ffi::apple_log_os_activity_end(self.ptr.as_ptr()) };
183 }
184
185 pub fn label_user_action(label: &str) {
186 let label = sanitized_c_string(label);
187 unsafe { ffi::apple_log_os_activity_label_useraction(label.as_ptr()) };
188 }
189
190 pub fn set_breadcrumb(name: &str) {
191 let name = sanitized_c_string(name);
192 unsafe { ffi::apple_log_os_activity_set_breadcrumb(name.as_ptr()) };
193 }
194
195 pub(crate) const fn as_ptr(&self) -> *mut c_void {
196 self.ptr.as_ptr()
197 }
198}
199
200impl Drop for OSActivity {
201 fn drop(&mut self) {
202 unsafe { ffi::apple_log_os_activity_release(self.ptr.as_ptr()) };
203 }
204}
205
206#[must_use]
208pub fn active_activity_id() -> u64 {
209 active_activity_ids().current
210}
211
212#[must_use]
214pub fn active_activity_ids() -> ActivityIds {
215 let mut parent = 0_u64;
216 let current = unsafe { ffi::apple_log_os_activity_get_active_identifiers(&mut parent) };
217 ActivityIds {
218 current,
219 parent: (parent != 0).then_some(parent),
220 }
221}