From e4ff89fee9da73aac01ec15ef81211f2054bf906 Mon Sep 17 00:00:00 2001 From: Florian Briand Date: Mon, 30 Sep 2024 23:40:55 +0200 Subject: [PATCH] feat: Gestion des versions multiples de FSV dans le wrapper exposant les fonctions de la librairie --- crates/fsv-sys/src/bindings.rs | 12 ++ crates/fsv-sys/src/lib.rs | 207 ++++++++++++++++++++------------- 2 files changed, 137 insertions(+), 82 deletions(-) create mode 100644 crates/fsv-sys/src/bindings.rs diff --git a/crates/fsv-sys/src/bindings.rs b/crates/fsv-sys/src/bindings.rs new file mode 100644 index 0000000..bac50d3 --- /dev/null +++ b/crates/fsv-sys/src/bindings.rs @@ -0,0 +1,12 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(unused)] + +pub mod BINDINGS_V1_40_14 { + include!(concat!(env!("OUT_DIR"), "/bindings_1.40.14.rs")); +} + +pub mod BINDINGS_V1_40_13 { + include!(concat!(env!("OUT_DIR"), "/bindings_1.40.13.rs")); +} diff --git a/crates/fsv-sys/src/lib.rs b/crates/fsv-sys/src/lib.rs index 6efdc5f..86f07a4 100644 --- a/crates/fsv-sys/src/lib.rs +++ b/crates/fsv-sys/src/lib.rs @@ -1,14 +1,17 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] #![allow(non_snake_case)] -#[derive(Debug)] -pub enum SUPPORTED_FSV_VERSIONS { +use std::marker::PhantomData; + +mod bindings; +use bindings::*; + +#[derive(Debug, Clone)] +pub enum SupportedFsvVersion { V1_40_14, // 1.40.14 V1_40_13, // 1.40.13 } -impl SUPPORTED_FSV_VERSIONS { +impl SupportedFsvVersion { fn as_str(&self) -> &'static str { match self { Self::V1_40_14 => "1.40.14", @@ -17,32 +20,6 @@ impl SUPPORTED_FSV_VERSIONS { } } -pub mod BINDINGS { - include!(concat!(env!("OUT_DIR"), "/bindings_1.40.13.rs")); -} - -// We need to keep the this use statement to get `ssv_function` macro working well -use BINDINGS::*; - -/// Macro to generate a function that implements a call to an external function in BINDINGS -macro_rules! ssv_function { - ($binding:ty, $func_name:ident, {$($arg_name:ident: $arg_type:ty),*}) => { - /// # Safety - /// This function is unsafe because it calls an external function through FFI. - /// The caller must ensure that the provided arguments are valid and that the - /// external function is safe to call. - pub unsafe fn $func_name(&self, $($arg_name: $arg_type),*) -> Result { - let func_struct: libloading::Symbol<'_, $binding> = - unsafe { self.library.get(stringify!($binding).as_bytes())? }; - let func = match *func_struct { - Some(func) => func, - None => return Err(Error::SymbolMissing(stringify!($binding))), - }; - Ok(func($($arg_name),*)) - } - }; -} - #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] @@ -51,15 +28,121 @@ pub enum Error { SymbolMissing(&'static str), } +/// Macro to generate a function that implements a call to an external function in BINDINGS +macro_rules! ssv_function { + ($binding:ty, $func_name:ident, {$($arg_name:ident: $arg_type:ty),*}) => { + /// # Safety + /// This function is unsafe because it calls an external function through FFI. + /// The caller must ensure that the provided arguments are valid and that the + /// external function is safe to call. + pub unsafe fn $func_name(&self, $($arg_name: $arg_type),*) -> Result { + let symbol_name = match stringify!($binding) + .split(&[' ', ':']) + .last() { + Some(name) => name, + None => return Err(Error::SymbolMissing(stringify!($binding))), + }; + let func_struct: libloading::Symbol<'_, $binding> = + unsafe { self.library.get(symbol_name.as_bytes())? }; + let func = match *func_struct { + Some(func) => func, + None => return Err(Error::SymbolMissing(stringify!($binding))), + }; + Ok(func($($arg_name),*)) + } + }; +} + +/// `sealed::Sealed` trait is used to prevent external crates from implementing the LibVersion trait. +mod sealed { pub trait Sealed {}} + /// Wrapper around the SESAM-VITALE library /// This struct is responsible for loading the library and providing an interface to call its functions. /// The library is loaded at creation and kept in memory until the struct is dropped. -#[derive(Debug)] -pub struct SSVLibrary { - version: SUPPORTED_FSV_VERSIONS, +pub trait SSVLibraryCommon { + fn new(path: &str) -> Result where Self: Sized; +} + +pub trait SSVLibraryVersion: sealed::Sealed {} + +pub struct V1_40_13 {} +impl sealed::Sealed for V1_40_13 {} +impl SSVLibraryVersion for V1_40_13 {} + +pub struct V1_40_14 {} +impl sealed::Sealed for V1_40_14 {} +impl SSVLibraryVersion for V1_40_14 {} + +pub struct SSVLibrary { + _version: PhantomData, library: libloading::Library, } +impl SSVLibraryCommon for SSVLibrary { + fn new(path: &str) -> Result { + let library = unsafe { libloading::Library::new(path)?}; + Ok(Self { + _version: PhantomData, + library + }) + } +} + +impl SSVLibrary { + pub fn library(&self) -> &libloading::Library { + &self.library + } + + ssv_function!(BINDINGS_V1_40_14::SSV_InitLIB2, ssv_init_lib2, { + pcFichierSesam: *const i8 + }); + + ssv_function!(BINDINGS_V1_40_14::SSV_LireConfig, ssv_lire_config, { + pZDataOut: *mut *mut libc::c_void, + psTailleDataOut: *mut usize + }); + + ssv_function!(BINDINGS_V1_40_14::SSV_LireCartePS, ssv_lire_carte_ps, { + NomRessourcePS: *const i8, + NomRessourceLecteur: *const i8, + CodePorteurPS: *const i8, + pZDataOut: *mut *mut libc::c_void, + pTailleZone: *mut usize + }); +} + +impl SSVLibrary { + ssv_function!(BINDINGS_V1_40_13::SSV_InitLIB2, ssv_init_lib2, { + pcFichierSesam: *const i8 + }); + + ssv_function!(BINDINGS_V1_40_13::SSV_LireConfig, ssv_lire_config, { + pZDataOut: *mut *mut libc::c_void, + psTailleDataOut: *mut usize + }); + + ssv_function!(BINDINGS_V1_40_13::SSV_LireCartePS, ssv_lire_carte_ps, { + NomRessourcePS: *const i8, + NomRessourceLecteur: *const i8, + CodePorteurPS: *const i8, + pZDataOut: *mut *mut libc::c_void, + pTailleZone: *mut usize + }); +} + +pub fn get_library_path(version: &SupportedFsvVersion) -> String { + let root_path = get_library_root_path(); + let library_name = get_library_name(); + let version = version.as_str(); + format!("{root_path}/{version}/lib/{library_name}") +} + +pub fn sesam_ini_path(version: &SupportedFsvVersion) -> String { + let root_path = get_sesam_ini_root_path(); + let version = version.as_str(); + format!("{root_path}/{version}/conf/sesam.ini") +} + fn get_library_name() -> &'static str { // TODO : Use libloading::library_filename to get platform-specific filename ? "libssvlux64.so" @@ -73,48 +156,6 @@ fn get_sesam_ini_root_path() -> &'static str { "/etc/opt/santesocial/fsv" } -impl SSVLibrary { - pub fn new(version: SUPPORTED_FSV_VERSIONS) -> Result { - let library_path = Self::get_library_path(&version); - let library = unsafe { libloading::Library::new(library_path)? }; - Ok(SSVLibrary { version, library }) - } - - pub fn library(&self) -> &libloading::Library { - &self.library - } - - pub fn sesam_ini_path(&self) -> String { - let root_path = get_sesam_ini_root_path(); - let version = self.version.as_str(); - format!("{root_path}/{version}/conf/sesam.ini") - } - - pub fn get_library_path(version: &SUPPORTED_FSV_VERSIONS) -> String { - let root_path = get_library_root_path(); - let library_name = get_library_name(); - let version = version.as_str(); - format!("{root_path}/{version}/lib/{library_name}") - } - - ssv_function!(SSV_InitLIB2, ssv_init_lib2, { - pcFichierSesam: *const i8 - }); - - ssv_function!(SSV_LireConfig, ssv_lire_config, { - pZDataOut: *mut *mut libc::c_void, - psTailleDataOut: *mut usize - }); - - ssv_function!(SSV_LireCartePS, ssv_lire_carte_ps, { - NomRessourcePS: *const i8, - NomRessourceLecteur: *const i8, - CodePorteurPS: *const i8, - pZDataOut: *mut *mut libc::c_void, - pTailleZone: *mut usize - }); -} - #[cfg(test)] mod test { use std::{ffi::CString, ptr}; @@ -123,22 +164,22 @@ mod test { #[test] fn test_initlib2() { - let ssv_library = - SSVLibrary::new(SUPPORTED_FSV_VERSIONS::V1_40_13).expect("SSVLibrary::new failed"); + let lib_path = &get_library_path(&SupportedFsvVersion::V1_40_13); + let ssv_library = SSVLibrary::::new(lib_path).expect("SSVLibrary::new failed"); let sesam_ini_str = - CString::new(ssv_library.sesam_ini_path()).expect("CString::new failed"); + CString::new(sesam_ini_path(&SupportedFsvVersion::V1_40_13)).expect("CString::new failed"); let result = unsafe { ssv_library.ssv_init_lib2(sesam_ini_str.as_ptr()) }.unwrap(); assert_eq!(result, 0); } #[test] fn test_lire_config_and_carte_ps() { - let ssv_library = - SSVLibrary::new(SUPPORTED_FSV_VERSIONS::V1_40_13).expect("SSVLibrary::new failed"); + let lib_path = &get_library_path(&SupportedFsvVersion::V1_40_13); + let ssv_library = SSVLibrary::::new(lib_path).expect("SSVLibrary::new failed"); let sesam_ini_str = - CString::new(ssv_library.sesam_ini_path()).expect("CString::new failed"); + CString::new(sesam_ini_path(&SupportedFsvVersion::V1_40_13)).expect("CString::new failed"); let result = unsafe { ssv_library.ssv_init_lib2(sesam_ini_str.as_ptr()) }.unwrap(); assert_eq!(result, 0); @@ -146,6 +187,7 @@ mod test { let mut size: libc::size_t = 0; let result = unsafe { ssv_library.ssv_lire_config(&mut buffer_ptr, &mut size) }.unwrap(); assert_eq!(result, 0); + unsafe { libc::free(buffer_ptr) }; let nom_ressource_ps = CString::new("Gemalto PC Twin Reader (645D94C3) 00 00").expect("CString::new failed"); @@ -165,5 +207,6 @@ mod test { } .unwrap(); assert_eq!(result, 0); + unsafe { libc::free(buffer_ptr) }; } }