From 9829a189a42f5f92ed91bed86d85795eb32c0fe4 Mon Sep 17 00:00:00 2001 From: theo Date: Thu, 29 Aug 2024 00:47:08 +0200 Subject: [PATCH] feat: add first draft for crate public api --- crates/services-sesam-vitale-sys/src/lib.rs | 128 +++---------- crates/services-sesam-vitale-sys/src/types.rs | 176 ------------------ .../src/types/serialization_types.rs | 127 +++++++++++-- 3 files changed, 135 insertions(+), 296 deletions(-) delete mode 100644 crates/services-sesam-vitale-sys/src/types.rs diff --git a/crates/services-sesam-vitale-sys/src/lib.rs b/crates/services-sesam-vitale-sys/src/lib.rs index 8b4323e..ab31ed1 100644 --- a/crates/services-sesam-vitale-sys/src/lib.rs +++ b/crates/services-sesam-vitale-sys/src/lib.rs @@ -1,122 +1,46 @@ -// to include std in docs, need to remove later -#[doc(inline)] -pub use std; - mod bindings; pub mod types; -use std::io::Cursor; use bindings::SSV_LireConfig; -use binrw::BinRead; -use std::ptr; -use types::serialization_types::{DataBlock, DataField}; +use std::{fmt, ptr}; +use types::serialization_types::{read_from_buffer, Configuration}; -//pub fn read_carte_professionnel_sante() -> Result { -// // how to init buffer and give it to library -// // https://stackoverflow.com/questions/58231215/what-is-proper-rust-way-to-allocate-opaque-buffer-for-external-c-library -// // -// // when init memory zones and they are too large to be a single memory zone -> https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0c1f0fca7d98a97bbc70dba786bbedd9 -// unsafe { -// let nom_ressource_ps; -// let nom_ressource_lecteur; -// let code_porteur_ps; -// let p_zdata_out; -// let p_taille_zone; -// let status_code: u16 = SSV_LireCartePS( -// nom_ressource_ps, -// nom_ressource_lecteur, -// code_porteur_ps, -// p_zdata_out, -// p_taille_zone, -// ); -// -// if status_code != 0 { -// return Err(()); -// } -// }; -//} - -// To parse the data -// allocate the multiple buffers -// chain them to make a single buffer -// use the parse_data_size function to get a size -// use take method to limit number of bytes read -// use binread implementaiton on each struct/enum de structure it -// do this recursively until there is no more data - -// Memory has three embricked concepts: -// Memory Zone(s) -Contains-> DataBlock(s) -Contains-> DataField(s) -// DataBlocks (and DataFields) can be cut off by the end of a memory zone -// the data continues on the following memory zone -//#[binread] -pub struct DataBlock2 { - // { - data_struct_id: u16, - - // #[br(temp, parse_with = parse_data_size)] - memory_size: u32, - // spec indicates the DataBlock can be very large (up to 4GB) - // in this case, we can use memmap2 to use the disk to store the data - // pub data: Vec>, +#[derive(Debug)] +pub struct SesamVitaleError { + code: u16, } -pub enum SSVError { - Error(u16), -} - -struct Parseable(T); - -impl BinRead for Parseable -where - for<'a> T: BinRead = ()>, -{ - type Args<'a> = as BinRead>::Args<'a>; - - fn read_options( - reader: &mut R, - endian: binrw::Endian, - args: Self::Args<'_>, - ) -> binrw::prelude::BinResult { - let field = DataField::::read_options(reader, endian, args)?; - Ok(Parseable(field.value)) +impl fmt::Display for SesamVitaleError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Got error code {} from SSV_LireConfig", self.code) } } -#[derive(BinRead)] -struct ConfigHeader { - ssv_version: Parseable, - galss_version: Parseable, - pss_version: Parseable, -} -#[derive(BinRead)] -struct ReaderConfig { - // manufacturer_name: Parseable -} - -pub fn read_config() -> Result<(), SSVError> { +pub fn read_config() -> Result { let mut buffer_ptr: *mut libc::c_void = ptr::null_mut(); let mut size: libc::size_t = 0; - let buffer: &[u8] = unsafe { - match SSV_LireConfig(&mut buffer_ptr, &mut size) { - 0 => (), - error_code => return Err(SSVError::Error(error_code)), - } + let buffer_ptr_ptr: *mut *mut libc::c_void = &mut buffer_ptr; + let size_ptr: *mut libc::size_t = &mut size; - std::slice::from_raw_parts(buffer_ptr as *const u8, size) + // Need to add proper error handling -> return a result with error code pointing to an error + // enum + let exit_code: u16 = unsafe { SSV_LireConfig(buffer_ptr_ptr, size_ptr) }; + + if exit_code != 0 { + let error = SesamVitaleError { code: exit_code }; + return Err(error); }; + + let buffer: &[u8] = unsafe { std::slice::from_raw_parts(buffer_ptr as *const u8, size) }; + + // TODO: Improve error handling + let configuration: Configuration = read_from_buffer(buffer).unwrap(); + + // TODO: Call library function for memory delocating unsafe { libc::free(buffer_ptr) }; - println!("Buffer data: {:?}", buffer); - - let cursor = &mut Cursor::new(buffer); - while size > 0 { - let data_block = DataBlock::read(cursor).expect(""); - size -= data_block.data.len(); - println!("{}", String::from_utf8(data_block.data).expect("")); - } - - Ok(()) + Ok(configuration) } #[cfg(test)] diff --git a/crates/services-sesam-vitale-sys/src/types.rs b/crates/services-sesam-vitale-sys/src/types.rs deleted file mode 100644 index 2712402..0000000 --- a/crates/services-sesam-vitale-sys/src/types.rs +++ /dev/null @@ -1,176 +0,0 @@ -struct Identification { - value: T, - // Key to check the validity of the value - // TODO: implement checking algorithm - key: u8, -} -type Byte = u8; - -enum IdentificationNationale { - NumeroAdeli(String), - NumeroEmployeeDansStructure(IdentificationStructure, String), - NumeroDRASS(String), - NumeroRPPS(String), - /// N° Etudiant Médecin type ADELI sur 9 caractères (information transmise par l’ANS) - NumeroEtudiantMedecin(String), -} - -enum TypeCarteProfessionnelSante { - /// Carte de Professionnel de Santé (CPS) - CarteDeProfessionnelSante, - /// Carte de Professionnel de Santé en Formation (CPF) - CarteDeProfessionnelSanteEnFormation, - /// Carte de Personnel d'Établissement de Santé (CDE/CPE) - CarteDePersonnelEtablissementSante, - /// Carte de Personnel Autorisé (CDA/CPA) - CarteDePersonnelAutorise, - /// Carte de Personne Morale - CarteDePersonneMorale, -} - -enum CategorieCarteProfessionnelSante { - Reelle, - Test, - Demonstration, -} - -enum CodeCivilite { - Adjudant, - Amiral, - Aspirant, - Aumônier, - Capitaine, - Cardinal, - Chanoine, - Colonel, - Commandant, - Commissaire, - Conseiller, - Directeur, - Docteur, - Douanier, - Epouxse, // Epoux(se) - Evêque, - Général, - Gouverneur, - Ingénieur, - Inspecteur, - Lieutenant, - Madame, - Mademoiselle, - Maître, - Maréchal, - Médecin, - Mesdames, - Mesdemoiselles, - Messieurs, - Monseigneur, - Monsieur, - NotreDame, - Pasteur, - Préfet, - Président, - Professeur, - Recteur, - Sergent, - SousPréfet, - Technicien, - Veuve, -} - -struct CarteProfessionnelSante { - type_carte: TypeCarteProfessionnelSante, - categorie_carte: CategorieCarteProfessionnelSante, - professionnel_sante: ProfessionnelDeSante, -} - -struct ProfessionnelDeSante { - prenom: String, - nom: String, - code_civilite: CodeCivilite, - identification_nationale: Identification, - situations_execice: Vec, -} - -enum IdentificationStructure { - NumeroAdeliCabinet(String), - NumeroFINESS(String), - NumeroSIREN(String), - NumeroSIRET(String), - NumeroRPPSCabinet(String), -} - -struct StructureMedicale { - identification: Identification, - raison_sociale: String, // Nom Entreprise -} - -enum ModeExercice { - LiberalExploitantCommercant, // Libéral, exploitant, commerçant - Salarie, - Remplacant, - Benevole, -} - -enum StatutExercice { - // TAB-Statuts géré par l’ANS il faut trouver la donnee - PLACEHOLDER(u8), -} - -enum SecteurActivite { - EtablissementPublicDeSanté, - HopitauxMilitaires, - EtablissementPrivePSPH, // Participant au Service Public Hospitalier - EtablissementPriveNonPSPH, - DispensaireDeSoins, - AutresStructuresDeSoinsRelevantDuServiceDeSanteDesArmees, - CabinetIndividuel, - CabinetDeGroupe, - ExerciceEnSociete, - SecteurPrivePHTempsPlein, - TransportSanitaire, - EntrepriseDInterim, - EtablissementDeSoinsEtPrevention, - PreventionEtSoinsEnEntreprise, - SanteScolaireEtUniversitaire, - RecrutementEtGestionRH, - PMIPlanificationFamiliale, - EtablissementPourHandicapes, - ComMarketingConsultingMedia, - EtablissementPersonnesAgees, - EtablissementAideaLaFamille, - EtablissementDEnseignement, - EtablissementsDeProtectionDeLEnfance, - EtablissementsDHebergementEtDeReadaptation, - Recherche, - AssurancePrivee, - OrganismeDeSecuriteSociale, - MinistèreEtServicesDeconcentres, - CollectivitesTerritoriales, - AssociationsEtOrganitationsHumanitaire, - LaboratoireDeBiologieMedicale, - AutreEtablissementSanitaire, - ProductionCommercialisationGrosBienMedicaux, - CommerceDétailDeBiensMédicaux, - PharmacieDOfficine, - CentreDeDialyse, - ParaPharmacie, - AutreSecteurDActivité, - SecteurNonDefini, - CentreAntiCancer, - CentreDeTransfusionSanguine, - RépartitionDistribributionFabricationExploitationImportationMedicamentsEtDispositifsMédicaux, - IncendiesEtSecours, - EntreprisesIndustriellesEtTertiairesHorsIndustriesPharmaceutiques, - - EntiteDUnTOM, - FabricationExploitationImportationMedicamentsEtDispositifsMedicaux, -} -struct SituationDExercice { - /// Numéro identifiant la situation du PS parmi ses autres situations inscrites sur sa CPS - identifiant_situation: Byte, - mode_exercice: Option, - statut_exercice: Option, - secteur_activite: Option, - structure_d_exercice: Option, -} diff --git a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs index e28812d..470f252 100644 --- a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs +++ b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs @@ -1,18 +1,14 @@ use bitvec::index::BitIdx; -use std::{error::Error, str::FromStr, vec::Vec}; +use std::{error::Error, fmt, str::FromStr, vec::Vec}; use deku::{ - bitvec::{BitStore, Msb0}, - ctx::ByteSize, - deku_derive, - reader::{Reader, ReaderRet}, - DekuError, DekuReader, + bitvec::{BitStore, Msb0}, ctx::ByteSize, deku_derive, reader::{Reader, ReaderRet}, DekuContainerRead, DekuError, DekuReader }; #[deku_derive(DekuRead)] #[derive(Debug, Clone, Copy, PartialEq)] #[deku(endian = "big")] -pub struct GroupId(u16); +pub(crate) struct GroupId(u16); trait MapToDekuParseError { fn map_to_deku_parse_error(self) -> Result; @@ -26,30 +22,30 @@ impl MapToDekuParseError for Result { #[deku_derive(DekuRead)] #[derive(Debug, PartialEq)] -pub struct DataField { +pub(crate) struct DataField { #[deku(reader = "read_size(deku::reader)")] - data_size: ByteSize, + pub(crate) data_size: ByteSize, #[deku(bytes_read = "data_size.0")] - pub data: Vec, + pub(crate) data: Vec, } #[deku_derive(DekuRead)] #[derive(Debug, PartialEq)] -pub struct BlockHeader { - pub group_id: GroupId, +pub(crate) struct BlockHeader { + pub(crate) group_id: GroupId, #[deku(reader = "read_size(deku::reader)")] - pub data_size: ByteSize, + pub(crate) data_size: ByteSize, } #[deku_derive(DekuRead)] #[derive(Debug, PartialEq)] -pub struct DataBlock { - pub header: BlockHeader, +pub(crate) struct DataBlock { + pub(crate) header: BlockHeader, #[deku(ctx = "header.group_id")] - pub inner: DataGroup, + pub(crate) inner: DataGroup, } fn read_size(reader: &mut Reader) -> Result { @@ -144,14 +140,109 @@ pub struct SESAMVitaleComponent { pub version: SESAMVitaleComponentVersion, } +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct ReaderConfiguration {} + #[deku_derive(DekuRead)] #[derive(Debug, PartialEq)] #[deku(ctx = "group_id: GroupId", id = "group_id.0")] pub enum DataGroup { #[deku(id = 60)] ConfigurationHeader(ConfigurationHeader), - #[deku(id = 67)] - PCSCReader(PCSCReader), + #[deku(id = 61)] + ReaderConfiguration(ReaderConfiguration), #[deku(id = 64)] SESAMVitaleComponent(SESAMVitaleComponent), + #[deku(id = 67)] + PCSCReader(PCSCReader), +} + +#[derive(Debug)] +pub enum ConfigurationError { + MultipleConfigurationHeaders, + MissingConfigurationHeader, +} + +impl fmt::Display for ConfigurationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ConfigurationError::MultipleConfigurationHeaders => { + write!(f, "Multiple ConfigurationHeader blocks found") + } + ConfigurationError::MissingConfigurationHeader => { + write!(f, "Missing ConfigurationHeader block") + } + } + } +} + +impl Error for ConfigurationError {} + +#[derive(Debug)] +pub struct Configuration { + pub configuration_header: ConfigurationHeader, + pub reader_configurations: Vec, + pub sesam_vitale_components: Vec, + pub pcsc_readers: Vec, +} + +impl TryFrom> for Configuration { + type Error = ConfigurationError; + + fn try_from(data_blocks: Vec) -> Result { + let mut configuration_header: Option = None; + let mut reader_configurations: Vec = Vec::new(); + let mut sesam_vitale_components: Vec = Vec::new(); + let mut pcsc_readers: Vec = Vec::new(); + + for block in data_blocks { + match block.inner { + DataGroup::ConfigurationHeader(header) => { + if configuration_header.is_some() { + return Err(ConfigurationError::MultipleConfigurationHeaders); + } + configuration_header = Some(header); + } + DataGroup::ReaderConfiguration(configuration) => { + reader_configurations.push(configuration) + } + DataGroup::SESAMVitaleComponent(component) => { + sesam_vitale_components.push(component); + } + DataGroup::PCSCReader(reader) => { + pcsc_readers.push(reader); + } + } + } + let configuration_header = match configuration_header { + Some(header) => header, + None => return Err(ConfigurationError::MissingConfigurationHeader), + }; + + Ok(Self { + configuration_header, + reader_configurations, + sesam_vitale_components, + pcsc_readers, + }) + } +} + +pub(crate) fn read_from_buffer>>(buffer: &[u8]) -> Result{ + let mut data_blocks: Vec = Vec::new(); + let mut offset = 0; + + let mut remaining_buffer = buffer; + + while !remaining_buffer.is_empty() { + // TODO: properly handle errors + let (rest, data_block) = DataBlock::from_bytes((remaining_buffer, offset)).unwrap(); + + data_blocks.push(data_block); + + (remaining_buffer, offset) = rest; + }; + + T::try_from(data_blocks) }