From 22ee2725943bf9b0b53e2501f2d80e31b1f486d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20R=C3=A9veillon?= Date: Mon, 22 Nov 2021 17:37:39 +0100 Subject: [PATCH] macro to create linked list handling structure --- Cargo.toml | 1 + src/core.rs | 123 +++++++++---------------------- src/tools.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 90 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b53bcfa..31a3994 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ crate-type = ["rlib"] [dependencies] libc = "0.2" libvlc-sys = "0.2" +paste = "1.0" [target.'cfg(target_os = "windows")'.build-dependencies] vswhom = "0.1.0" \ No newline at end of file diff --git a/src/core.rs b/src/core.rs index 93c8c9b..c5afa28 100644 --- a/src/core.rs +++ b/src/core.rs @@ -3,14 +3,13 @@ // Licensed under the MIT license, see the LICENSE file. use crate::enums::*; -use crate::tools::{from_cstr, from_cstr_ref, to_cstr}; +use crate::tools::{from_cstr, from_cstr_ref, linked_list_iter, to_cstr}; use libc::{c_char, c_int, c_void}; use std::borrow::Cow; use std::convert::TryInto; use std::ffi::CString; use std::fmt; use std::i32; -use std::marker::PhantomData; use std::ptr; use vlc_sys as sys; @@ -25,6 +24,36 @@ impl fmt::Display for InternalError { impl std::error::Error for InternalError {} + +linked_list_iter!( + libvlc_module_description, // C raw type + module_description, // Rust base struct name + { + name: (String, str), + shortname: (String, str), + longname: (String, str), + help: (String, str), + } // fields +); + +linked_list_iter!( + libvlc_audio_output, // C raw type + audio_output, // Rust base struct name + { + name: (String, str), + description: (String, str), + } // fields +); + +linked_list_iter!( + libvlc_audio_output_device, // C raw type + audio_output_device, // Rust base struct name + { + device: (String, str), + description: (String, str), + } // fields +); + /// Retrieve libvlc version. pub fn version() -> String { unsafe { @@ -131,7 +160,7 @@ impl Instance { if p.is_null() { None } else { - Some(ModuleDescriptionList { ptr: p }) + Some(ModuleDescriptionList::new(p)) } } } @@ -143,7 +172,7 @@ impl Instance { if p.is_null() { None } else { - Some(ModuleDescriptionList { ptr: p }) + Some(ModuleDescriptionList::new(p)) } } } @@ -203,92 +232,6 @@ unsafe extern "C" fn logging_cb( ); } -/// List of module description. -pub struct ModuleDescriptionList { - ptr: *mut sys::libvlc_module_description_t, -} - -impl ModuleDescriptionList { - /// Returns raw pointer - pub fn raw(&self) -> *mut sys::libvlc_module_description_t { - self.ptr - } -} - -impl Drop for ModuleDescriptionList { - fn drop(&mut self) { - unsafe { sys::libvlc_module_description_list_release(self.ptr) }; - } -} - -impl<'a> IntoIterator for &'a ModuleDescriptionList { - type Item = ModuleDescriptionRef<'a>; - type IntoIter = ModuleDescriptionListIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - ModuleDescriptionListIter { - ptr: self.ptr, - _phantomdata: PhantomData, - } - } -} - -pub struct ModuleDescriptionListIter<'a> { - ptr: *mut sys::libvlc_module_description_t, - _phantomdata: PhantomData<&'a sys::libvlc_module_description_t>, -} - -/// Description of a module. -/// The strings are owned. -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct ModuleDescription { - pub name: Option, - pub shortname: Option, - pub longname: Option, - pub help: Option, -} - -/// Description of a module. -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct ModuleDescriptionRef<'a> { - pub name: Option>, - pub shortname: Option>, - pub longname: Option>, - pub help: Option>, -} - -impl<'a> Iterator for ModuleDescriptionListIter<'a> { - type Item = ModuleDescriptionRef<'a>; - - fn next(&mut self) -> Option { - unsafe { - if self.ptr.is_null() { - return None; - } - let p = self.ptr; - self.ptr = (*p).p_next; - Some(ModuleDescriptionRef { - name: from_cstr_ref((*p).psz_name), - shortname: from_cstr_ref((*p).psz_shortname), - longname: from_cstr_ref((*p).psz_longname), - help: from_cstr_ref((*p).psz_help), - }) - } - } -} - -impl<'a> ModuleDescriptionRef<'a> { - /// Convert to owned strings. - pub fn into_owned(&'a self) -> ModuleDescription { - ModuleDescription { - name: self.name.as_ref().map(|s| s.clone().into_owned()), - shortname: self.shortname.as_ref().map(|s| s.clone().into_owned()), - longname: self.name.as_ref().map(|s| s.clone().into_owned()), - help: self.shortname.as_ref().map(|s| s.clone().into_owned()), - } - } -} - pub fn errmsg() -> Option { unsafe { from_cstr(sys::libvlc_errmsg()) } } diff --git a/src/tools.rs b/src/tools.rs index abc6ea6..82ea5da 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -41,3 +41,204 @@ pub fn path_to_cstr(path: &Path) -> Result { Ok(path) } + +/// Defines a module with all necessary structures to easily handle a C linked list +macro_rules! linked_list_iter { + ( + $c_type:ident, + $name:ident, + { + $($(#[$field_meta:meta])* + $field_vis:vis $field_name:ident: ($field_type:ty, $field_b_type:ty)),* $(,)+ + } + ) => { + paste::item! { + mod $name { + use std::borrow::Cow; + use std::marker::PhantomData; + use crate::tools::from_cstr_ref; + + use vlc_sys::[<$c_type _t>]; + use vlc_sys::[<$c_type _list_release>]; + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub struct Item { + $( + $(#[$field_meta:meta])* + $field_vis $field_name : Option<$field_type>, + )* + } + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub struct ItemRef<'a> { + $( + $(#[$field_meta:meta])* + $field_vis $field_name : Option>, + )* + } + + impl<'a> ItemRef<'a> { + /// Convert to owned strings. + pub fn into_owned(&'a self) -> Item { + Item { + $($field_name: self.$field_name.as_ref().map(|s| s.clone().into_owned()),)* + } + } + } + + pub struct ListIter<'a> { + ptr: *mut [<$c_type _t>], + _phantomdata: PhantomData<&'a [<$c_type _t>]>, + } + + impl<'a> Iterator for ListIter<'a> { + type Item = ItemRef<'a>; + + fn next(&mut self) -> Option { + unsafe { + if self.ptr.is_null() { + return None; + } + let p = self.ptr; + self.ptr = (*p).p_next; + Some(ItemRef { + $($field_name: from_cstr_ref((*p).[]),)* + }) + } + } + } + + pub struct List { + ptr: *mut [<$c_type _t>] + } + + impl List { + pub fn new(ptr: *mut [<$c_type _t>]) -> List { + Self { ptr } + } + + /// Returns raw pointer + pub fn raw(&self) -> *mut [<$c_type _t>] { + self.ptr + } + } + + impl Drop for List { + fn drop(&mut self) { + unsafe{ [<$c_type _list_release>](self.ptr) }; + } + } + + impl<'a> IntoIterator for &'a List { + type Item = ItemRef<'a>; + type IntoIter = ListIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + ListIter{ptr: self.ptr, _phantomdata: PhantomData} + } + } + } + + // backward compatibility types + pub type [<$name:camel>] = $name::Item; + pub type [<$name:camel Ref>]<'a> = $name::ItemRef<'a>; + pub type [<$name:camel List>] = $name::List; + pub type [<$name:camel ListIter>]<'a> = $name::ListIter<'a>; + } + } + } + + /* + macro_rules! linked_list_iter { + ( + $namespace:path, + $c_type: ident, + $name: ident, + { + $($(#[$field_meta:meta])* + $field_vis:vis $field_name:ident: ($field_type:ty, $field_b_type:ty)),* $(,)+ + } + ) => { + paste::item! { + use std::marker::PhantomData; + use crate::$namespace::[<$c_type _t>]; + use crate::$namespace::[<$c_type _list_release>]; + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub struct $name { + $( + $(#[$field_meta:meta])* + $field_vis $field_name : Option<$field_type>, + )* + } + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub struct [<$name Ref>]<'a> { + $( + $(#[$field_meta:meta])* + $field_vis $field_name : Option>, + )* + } + + impl<'a> [<$name Ref>]<'a> { + /// Convert to owned strings. + pub fn into_owned(&'a self) -> $name { + $name { + $($field_name: self.$field_name.as_ref().map(|s| s.clone().into_owned()),)* + } + } + } + + pub struct [<$name ListIter>]<'a> { + ptr: *mut [<$c_type _t>], + _phantomdata: PhantomData<&'a [<$c_type _t>]>, + } + + impl<'a> Iterator for [<$name ListIter>]<'a> { + type Item = [<$name Ref>]<'a>; + + fn next(&mut self) -> Option { + unsafe { + if self.ptr.is_null() { + return None; + } + let p = self.ptr; + self.ptr = (*p).p_next; + Some([<$name Ref>] { + $($field_name: from_cstr_ref((*p).[]),)* + }) + } + } + } + + pub struct [<$name List>] { + ptr: *mut [<$c_type _t>] + } + + impl [<$name List>] { + /// Returns raw pointer + pub fn raw(&self) -> *mut [<$c_type _t>] { + self.ptr + } + } + + impl Drop for [<$name List>] { + fn drop(&mut self) { + unsafe{ [<$c_type _list_release>](self.ptr) }; + } + } + + impl<'a> IntoIterator for &'a [<$name List>] { + type Item = [<$name Ref>]<'a>; + type IntoIter = [<$name ListIter>]<'a>; + + fn into_iter(self) -> Self::IntoIter { + [<$name ListIter>]{ptr: self.ptr, _phantomdata: PhantomData} + } + } + } + } + } + */ + + pub(crate) use linked_list_iter; \ No newline at end of file