use std::{
ffi::{CStr, CString},
fmt::Debug,
marker::PhantomData,
ptr,
};
use static_assertions::assert_impl_all;
use crate::{
bindings,
util::{SourceNameError, validate_source_name},
};
pub unsafe trait NDISourceLike: Debug {
fn with_descriptor<R>(&self, f: impl FnOnce(*const bindings::NDIlib_source_t) -> R) -> R;
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct NullPtrSource;
unsafe impl NDISourceLike for NullPtrSource {
fn with_descriptor<R>(&self, f: impl FnOnce(*const bindings::NDIlib_source_t) -> R) -> R {
f(ptr::null())
}
}
unsafe impl<S: NDISourceLike> NDISourceLike for Option<S> {
fn with_descriptor<R>(&self, f: impl FnOnce(*const bindings::NDIlib_source_t) -> R) -> R {
if let Some(source) = self {
source.with_descriptor(f)
} else {
NullPtrSource.with_descriptor(f)
}
}
}
#[derive(Clone)]
pub struct NDISourceRef<'a> {
name: &'a CStr,
raw: bindings::NDIlib_source_t,
raw_ptrs: PhantomData<&'a CStr>,
}
impl<'a> NDISourceRef<'a> {
pub(crate) unsafe fn from(source_t: bindings::NDIlib_source_t) -> Self {
if source_t.p_ndi_name.is_null() {
panic!("[Fatal FFI Error] NDI SDK returned nullptr for source name")
} else {
let name = unsafe { CStr::from_ptr(source_t.p_ndi_name) };
NDISourceRef {
name,
raw: source_t,
raw_ptrs: PhantomData,
}
}
}
pub fn name(&'a self) -> &'a CStr {
self.name
}
pub fn to_owned(&self) -> NDISource {
let descriptor_anon_1 = self.raw.__bindgen_anon_1;
let descriptor_anon_1: *const ::std::os::raw::c_char =
unsafe { descriptor_anon_1.p_url_address };
let descriptor_anon_1 = if descriptor_anon_1.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(descriptor_anon_1).to_owned() })
};
let name = self.name.to_owned();
NDISource {
name: name.clone().into_string().unwrap(),
name_c: name,
descriptor_anon_1,
}
}
}
unsafe impl NDISourceLike for &NDISourceRef<'_> {
fn with_descriptor<R>(&self, f: impl FnOnce(*const bindings::NDIlib_source_t) -> R) -> R {
f(&self.raw)
}
}
impl std::fmt::Debug for NDISourceRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let descriptor_anon_1 = self.raw.__bindgen_anon_1;
let descriptor_anon_1: *const ::std::os::raw::c_char =
unsafe { descriptor_anon_1.p_url_address };
let mut dbg = f.debug_struct("NDISourceRef");
dbg.field("name", &self.name.to_str());
if !descriptor_anon_1.is_null() {
let descriptor = unsafe { CStr::from_ptr(descriptor_anon_1) };
dbg.field("raw_src", &descriptor);
} else {
dbg.field("raw_src", &"null");
}
dbg.finish()
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct NDISource {
name: String,
name_c: CString,
descriptor_anon_1: Option<CString>,
}
assert_impl_all!(NDISource: Send, Sync);
impl NDISource {
pub fn from_name(name: &str) -> Result<Self, SourceNameError> {
let name_c = validate_source_name(name)?;
Ok(NDISource {
name: name.to_owned(),
name_c,
descriptor_anon_1: None,
})
}
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn name_c_str(&self) -> &CStr {
&self.name_c
}
}
unsafe impl NDISourceLike for NDISource {
fn with_descriptor<R>(&self, f: impl FnOnce(*const bindings::NDIlib_source_t) -> R) -> R {
let descriptor = bindings::NDIlib_source_t {
p_ndi_name: self.name_c.as_ptr(),
__bindgen_anon_1: bindings::NDIlib_source_t__bindgen_ty_1 {
p_url_address: self
.descriptor_anon_1
.as_ref()
.map(|s| s.as_ptr())
.unwrap_or(ptr::null()),
},
};
f(&descriptor)
}
}
unsafe impl NDISourceLike for &NDISource {
fn with_descriptor<R>(&self, f: impl FnOnce(*const bindings::NDIlib_source_t) -> R) -> R {
(*self).with_descriptor(f)
}
}
impl std::fmt::Debug for NDISource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NDISource")
.field("name", &self.name)
.field("raw_src", &self.descriptor_anon_1)
.finish()
}
}