use std::ffi::{CString, c_char}; unsafe extern "C" { fn ds4_media_open(url: *const c_char, title: *const c_char, video: bool) -> bool; } pub(crate) fn open(url: &str, title: &str, video: bool) -> Result<(), String> { let url = playable_url(url)?; let url = CString::new(url.as_str()).map_err(|error| error.to_string())?; let title = CString::new(title).map_err(|error| error.to_string())?; // SAFETY: Both C strings live for the duration of the call; Objective-C copies them. if unsafe { ds4_media_open(url.as_ptr(), title.as_ptr(), video) } { Ok(()) } else { Err("AVKit could not create the media player".into()) } } fn playable_url(value: &str) -> Result { let url = url::Url::parse(value).map_err(|error| format!("invalid media URL: {error}"))?; if matches!(url.scheme(), "http" | "https") { Ok(url) } else { Err("media URLs must use HTTP(S)".into()) } } #[cfg(test)] mod tests { use super::*; #[test] fn media_player_only_accepts_remote_http_urls() { assert!(playable_url("https://example.com/video.mp4").is_ok()); assert!(playable_url("http://example.com/audio.mp3").is_ok()); assert!(playable_url("file:///tmp/private.mov").is_err()); assert!(playable_url("javascript:alert(1)").is_err()); } }