1use std::{
2 cell::RefCell,
3 ffi::{CString, c_char, c_void},
4 sync::LazyLock,
5};
6
7use url::Url;
8
9use crate::{Error, Id, moq_protocol_error};
10
11#[allow(non_camel_case_types)]
13pub type moq_status_callback = Option<extern "C" fn(user_data: *mut c_void, code: i32)>;
14
15pub static RUNTIME: LazyLock<tokio::runtime::Handle> = LazyLock::new(|| {
16 let runtime = tokio::runtime::Builder::new_current_thread()
17 .enable_all()
18 .build()
19 .unwrap();
20 let handle = runtime.handle().clone();
21
22 std::thread::Builder::new()
23 .name("libmoq".into())
24 .spawn(move || {
25 runtime.block_on(std::future::pending::<()>());
26 })
27 .expect("failed to spawn runtime thread");
28
29 handle
30});
31
32pub fn enter<C: ReturnCode, F: FnOnce() -> C>(f: F) -> i32 {
40 let _guard = RUNTIME.enter();
41
42 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
43 Ok(ret) => {
44 record_error(&ret);
45 ret.code()
46 }
47 Err(_) => {
48 record_error(&Error::Panic);
49 Error::Panic.code()
50 }
51 }
52}
53
54#[derive(Clone, Copy)]
59pub struct OnStatus {
60 user_data: *mut c_void,
61 on_status: extern "C" fn(user_data: *mut c_void, code: i32),
62}
63
64impl OnStatus {
65 pub unsafe fn new(user_data: *mut c_void, on_status: moq_status_callback) -> Result<Self, Error> {
71 Ok(Self {
72 user_data,
73 on_status: on_status.ok_or(Error::InvalidPointer)?,
74 })
75 }
76
77 pub fn call<C: ReturnCode>(&self, ret: C) {
82 record_error(&ret);
83 let code = ret.code();
84 (self.on_status)(self.user_data, code);
85 }
86}
87
88unsafe impl Send for OnStatus {}
89
90pub trait ReturnCode {
92 fn code(&self) -> i32;
94
95 fn error(&self) -> Option<&Error> {
98 None
99 }
100}
101
102impl ReturnCode for () {
103 fn code(&self) -> i32 {
104 0
105 }
106}
107
108impl ReturnCode for i32 {
109 fn code(&self) -> i32 {
110 *self
111 }
112}
113
114impl ReturnCode for Result<i32, Error> {
115 fn code(&self) -> i32 {
116 match self {
117 Ok(code) if *code < 0 => Error::InvalidCode.code(),
118 Ok(code) => *code,
119 Err(e) => e.code(),
120 }
121 }
122
123 fn error(&self) -> Option<&Error> {
124 self.as_ref().err()
125 }
126}
127
128impl ReturnCode for Result<usize, Error> {
129 fn code(&self) -> i32 {
130 match self {
131 Ok(code) => i32::try_from(*code).unwrap_or_else(|_| Error::InvalidCode.code()),
132 Err(e) => e.code(),
133 }
134 }
135
136 fn error(&self) -> Option<&Error> {
137 self.as_ref().err()
138 }
139}
140
141impl ReturnCode for Result<Id, Error> {
142 fn code(&self) -> i32 {
143 match self {
144 Ok(id) => i32::from(*id),
145 Err(e) => e.code(),
146 }
147 }
148
149 fn error(&self) -> Option<&Error> {
150 self.as_ref().err()
151 }
152}
153
154impl ReturnCode for Result<(), Error> {
155 fn code(&self) -> i32 {
156 match self {
157 Ok(()) => 0,
158 Err(e) => e.code(),
159 }
160 }
161
162 fn error(&self) -> Option<&Error> {
163 self.as_ref().err()
164 }
165}
166
167impl ReturnCode for usize {
168 fn code(&self) -> i32 {
169 i32::try_from(*self).unwrap_or_else(|_| Error::InvalidCode.code())
170 }
171}
172
173impl ReturnCode for Id {
174 fn code(&self) -> i32 {
175 i32::from(*self)
176 }
177}
178
179struct LastError {
180 message: CString,
181 protocol: Option<moq_protocol_error>,
182}
183
184thread_local! {
185 static LAST_ERROR: RefCell<Option<LastError>> = const { RefCell::new(None) };
190}
191
192fn record_error<C: ReturnCode>(ret: &C) {
197 let Some(err) = ret.error() else { return };
198 if let Ok(msg) = CString::new(err.to_string()) {
201 LAST_ERROR.with(|cell| {
202 *cell.borrow_mut() = Some(LastError {
203 message: msg,
204 protocol: err.protocol(),
205 });
206 });
207 }
208}
209
210pub fn last_error_ptr() -> *const c_char {
214 LAST_ERROR.with(|cell| {
215 cell.borrow()
216 .as_ref()
217 .map_or(std::ptr::null(), |err| err.message.as_ptr())
218 })
219}
220
221pub fn last_protocol(out: &mut moq_protocol_error) -> bool {
225 LAST_ERROR.with(|cell| match cell.borrow().as_ref().and_then(|err| err.protocol) {
226 Some(protocol) => {
227 *out = protocol;
228 true
229 }
230 None => false,
231 })
232}
233
234pub fn parse_id(id: u32) -> Result<Id, Error> {
236 Id::try_from(id)
237}
238
239pub fn parse_id_optional(id: u32) -> Result<Option<Id>, Error> {
241 match id {
242 0 => Ok(None),
243 id => Ok(Some(parse_id(id)?)),
244 }
245}
246
247pub fn parse_url(url: *const c_char, url_len: usize) -> Result<Url, Error> {
249 let url = unsafe { parse_str(url, url_len)? };
250 Ok(Url::parse(url)?)
251}
252
253pub unsafe fn parse_str<'a>(cstr: *const c_char, cstr_len: usize) -> Result<&'a str, Error> {
260 let slice = unsafe { parse_slice(cstr.cast::<u8>(), cstr_len)? };
261 let string = std::str::from_utf8(slice)?;
262 Ok(string)
263}
264
265pub unsafe fn parse_str_optional<'a>(cstr: *const c_char, cstr_len: usize) -> Result<Option<&'a str>, Error> {
273 if cstr.is_null() {
274 return Ok(None);
275 }
276
277 let string = unsafe { parse_str(cstr, cstr_len)? };
278 Ok((!string.is_empty()).then_some(string))
279}
280
281pub unsafe fn parse_strings(items: *const crate::moq_string, count: usize) -> Result<Vec<String>, Error> {
289 if items.is_null() {
290 if count == 0 {
291 return Ok(Vec::new());
292 }
293
294 return Err(Error::InvalidPointer);
295 }
296
297 let items = unsafe { std::slice::from_raw_parts(items, count) };
298 items
299 .iter()
300 .map(|item| Ok(unsafe { parse_str(item.data, item.len)? }.to_string()))
301 .collect()
302}
303
304pub unsafe fn parse_slice<'a>(data: *const u8, size: usize) -> Result<&'a [u8], Error> {
311 if data.is_null() {
312 if size == 0 {
313 return Ok(&[]);
314 }
315
316 return Err(Error::InvalidPointer);
317 }
318
319 let data = unsafe { std::slice::from_raw_parts(data, size) };
320 Ok(data)
321}