From 3712667a046dbb6f1088ee050aa3bf60db1f86de Mon Sep 17 00:00:00 2001 From: theo Date: Sat, 3 Aug 2024 17:23:23 +0200 Subject: [PATCH 01/12] chore: init sys crate --- crates/services-sesam-vitale-sys/Cargo.toml | 6 ++++++ crates/services-sesam-vitale-sys/build.rs | 0 crates/services-sesam-vitale-sys/src/lib.rs | 14 ++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 crates/services-sesam-vitale-sys/Cargo.toml create mode 100644 crates/services-sesam-vitale-sys/build.rs create mode 100644 crates/services-sesam-vitale-sys/src/lib.rs diff --git a/crates/services-sesam-vitale-sys/Cargo.toml b/crates/services-sesam-vitale-sys/Cargo.toml new file mode 100644 index 0000000..c241e61 --- /dev/null +++ b/crates/services-sesam-vitale-sys/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "services-sesam-vitale-sys" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/crates/services-sesam-vitale-sys/build.rs b/crates/services-sesam-vitale-sys/build.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/services-sesam-vitale-sys/src/lib.rs b/crates/services-sesam-vitale-sys/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} -- 2.45.2 From f42e38228ab1e33203f473bd6ba81a335141d426 Mon Sep 17 00:00:00 2001 From: theo Date: Sun, 4 Aug 2024 01:24:42 +0200 Subject: [PATCH 02/12] chore: start implementing types from docs --- Cargo.toml | 1 + crates/services-sesam-vitale-sys/Cargo.toml | 2 - crates/services-sesam-vitale-sys/build.rs | 0 crates/services-sesam-vitale-sys/src/lib.rs | 3 + crates/services-sesam-vitale-sys/src/types.rs | 176 ++++++++++++++++++ 5 files changed, 180 insertions(+), 2 deletions(-) delete mode 100644 crates/services-sesam-vitale-sys/build.rs create mode 100644 crates/services-sesam-vitale-sys/src/types.rs diff --git a/Cargo.toml b/Cargo.toml index a4ff5d4..579fb99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,6 @@ members = [ "crates/backend", "crates/desktop", "crates/sesam-vitale", + "crates/services-sesam-vitale-sys", "crates/utils", ] diff --git a/crates/services-sesam-vitale-sys/Cargo.toml b/crates/services-sesam-vitale-sys/Cargo.toml index c241e61..21610d1 100644 --- a/crates/services-sesam-vitale-sys/Cargo.toml +++ b/crates/services-sesam-vitale-sys/Cargo.toml @@ -2,5 +2,3 @@ name = "services-sesam-vitale-sys" version = "0.1.0" edition = "2021" - -[dependencies] diff --git a/crates/services-sesam-vitale-sys/build.rs b/crates/services-sesam-vitale-sys/build.rs deleted file mode 100644 index e69de29..0000000 diff --git a/crates/services-sesam-vitale-sys/src/lib.rs b/crates/services-sesam-vitale-sys/src/lib.rs index 7d12d9a..0b4546e 100644 --- a/crates/services-sesam-vitale-sys/src/lib.rs +++ b/crates/services-sesam-vitale-sys/src/lib.rs @@ -1,3 +1,6 @@ +mod types; + +use types::*; pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/crates/services-sesam-vitale-sys/src/types.rs b/crates/services-sesam-vitale-sys/src/types.rs new file mode 100644 index 0000000..2712402 --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/types.rs @@ -0,0 +1,176 @@ +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, +} -- 2.45.2 From 20e16f6ae2b9b5633de07c819960811891715ba8 Mon Sep 17 00:00:00 2001 From: theo Date: Sun, 4 Aug 2024 23:02:12 +0200 Subject: [PATCH 03/12] feat: test different implementations to parse memory --- .../services-sesam-vitale-sys/src/bindings.rs | 285 ++++++++++++++++++ .../src/types/common.rs | 264 ++++++++++++++++ .../src/types/mod.rs | 3 + .../src/types/serialization_types.rs | 81 +++++ .../src/types/types.rs | 45 +++ 5 files changed, 678 insertions(+) create mode 100644 crates/services-sesam-vitale-sys/src/bindings.rs create mode 100644 crates/services-sesam-vitale-sys/src/types/common.rs create mode 100644 crates/services-sesam-vitale-sys/src/types/mod.rs create mode 100644 crates/services-sesam-vitale-sys/src/types/serialization_types.rs create mode 100644 crates/services-sesam-vitale-sys/src/types/types.rs diff --git a/crates/services-sesam-vitale-sys/src/bindings.rs b/crates/services-sesam-vitale-sys/src/bindings.rs new file mode 100644 index 0000000..c01f0ea --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/bindings.rs @@ -0,0 +1,285 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +extern "C" { + // Fonctions de gestion des données + + pub fn SSV_LireCartePS( + NomRessourcePS: *const ::std::os::raw::c_char, + NomRessourceLecteur: *const ::std::os::raw::c_char, + CodePorteurPS: *const ::std::os::raw::c_char, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_LireDroitsVitale( + NomRessourcePS: *const ::std::os::raw::c_char, + NomRessourceLecteur: *const ::std::os::raw::c_char, + CodePorteurPS: *const ::std::os::raw::c_char, + DateConsultation: *const ::std::os::raw::c_char, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_FormaterFactures( + cFactureACreer: ::std::os::raw::c_char, + cModeSecur: ::std::os::raw::c_char, + cTypeFlux: ::std::os::raw::c_char, + pZDataIn: *mut ::std::os::raw::c_void, + TailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_ChiffrerFacture( + pZDataIn: *mut ::std::os::raw::c_void, + TailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_SignerFactureVitale( + pcNomRessourceVitale: *const ::std::os::raw::c_char, + pZDataIn: *mut ::std::os::raw::c_void, + szTailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pszTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_CalculerHashFactureAssure( + pcNumSerie: *const ::std::os::raw::c_char, + pZDataIn: *mut ::std::os::raw::c_void, + szTailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pszTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_AjouterSignatureAssureDansFacture( + pZDataIn: *mut ::std::os::raw::c_void, + szTailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pszTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_SignerFactureCPS( + pcNomRessourcePS: *const ::std::os::raw::c_char, + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + pcCodePorteurPS: *const ::std::os::raw::c_char, + cNologSituation: ::std::os::raw::c_char, + pZDataIn: *mut ::std::os::raw::c_void, + szTailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pszTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_FormaterLot( + NBZDataIn: ::std::os::raw::c_short, + TZDataIn: *mut *mut ::std::os::raw::c_void, + TTailleZoneIn: *mut usize, + pNbZDataOut: *mut ::std::os::raw::c_short, + TZDataOut: *mut *mut ::std::os::raw::c_void, + TTailleZoneOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_SignerLotCPS( + pcNomRessourcePS: *const ::std::os::raw::c_char, + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + pcCodePorteurPS: *const ::std::os::raw::c_char, + cNologSituation: ::std::os::raw::c_char, + pZDataIn: *mut ::std::os::raw::c_void, + szTailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pszTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_FormaterFichier( + pZDataIn: *mut ::std::os::raw::c_void, + TailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_TraduireARL( + NbZDonneesEntree: ::std::os::raw::c_short, + TZDataIn: *mut *mut ::std::os::raw::c_void, + TTailleZoneIn: *mut usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pTailleZoneOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_LireNumSerieCarteVitale( + pcNomRessource: *mut ::std::os::raw::c_char, + numeroSerie: *mut ::std::os::raw::c_uchar, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_CalculerHashFacturePS( + pcNumSerieCPS: *const ::std::os::raw::c_char, + pZDataIn: *mut ::std::os::raw::c_void, + usTailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pusTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_AjouterSignaturePSFacture( + pZDataIn: *mut ::std::os::raw::c_void, + szTailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pszTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_DechargerFacturesPdT( + NomRessourcePS: *const ::std::os::raw::c_char, + NomRessourceLecteur: *const ::std::os::raw::c_char, + CodePorteurPS: *const ::std::os::raw::c_char, + pcNumFact: *const ::std::os::raw::c_char, + sNbZDataIn: ::std::os::raw::c_short, + pvTZDataIn: *mut *mut ::std::os::raw::c_void, + psTTailleDataIn: *mut usize, + pNbZDataOut: *mut ::std::os::raw::c_short, + TZDataOut: *mut *mut ::std::os::raw::c_void, + TTailleZoneOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_TraduireFSE( + pZDataIn: *mut ::std::os::raw::c_void, + TailleDataIn: usize, + pZDataOut: *mut *mut ::std::os::raw::c_void, + pTailleZone: *mut usize, + ) -> ::std::os::raw::c_ushort; + + // Fonctions TLA + // TLA (Terminal Lecteur Applicatif) -> lecteur autre que PC-SC, on ne prend pas en compte cela + + pub fn SSV_IdentifierTLA( + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + NumVersionCDC: *const ::std::os::raw::c_char, + pZDataOut: *mut *mut ::std::os::raw::c_void, + tailleDataOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_ChargerDonneesTLA( + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + sNbZDataIn: ::std::os::raw::c_short, + pvTZDataIn: *mut *mut ::std::os::raw::c_void, + psTTailleDataIn: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_ChargerFacturesPdT( + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + pcNumFacturation: *const ::std::os::raw::c_char, + sNbZDataIn: ::std::os::raw::c_short, + pvTZDataIn: *mut *mut ::std::os::raw::c_void, + psTTailleDataIn: *mut usize, + pNbZDataOut: *mut ::std::os::raw::c_short, + TZDataOut: *mut *mut ::std::os::raw::c_void, + TTailleZoneOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_DechargerFSETLA( + NomRessourcePS: *const ::std::os::raw::c_char, + NomRessourceLecteur: *const ::std::os::raw::c_char, + CodePorteurPS: *const ::std::os::raw::c_char, + pcNumFact: *const ::std::os::raw::c_char, + pNbZDataOut: *mut ::std::os::raw::c_short, + TZDataOut: *mut *mut ::std::os::raw::c_void, + TTailleZoneOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_DechargerFSETLANC( + NomRessourcePS: *const ::std::os::raw::c_char, + NomRessourceLecteur: *const ::std::os::raw::c_char, + CodePorteurPS: *const ::std::os::raw::c_char, + pcNumFact: *const ::std::os::raw::c_char, + pNbZDataOut: *mut ::std::os::raw::c_short, + TZDataOut: *mut *mut ::std::os::raw::c_void, + TTailleZoneOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_DechargerBeneficiaires( + NomRessourcePS: *const ::std::os::raw::c_char, + NomRessourceLecteur: *const ::std::os::raw::c_char, + CodePorteurPS: *const ::std::os::raw::c_char, + cNumFacturation: *const ::std::os::raw::c_char, + sNbZDataOut: *mut ::std::os::raw::c_short, + pTZDataOut: *mut *mut ::std::os::raw::c_void, + sTTailleDataOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_EffacerTLA( + NomRessourcePS: *const ::std::os::raw::c_char, + NomRessourceLecteur: *const ::std::os::raw::c_char, + CodePorteurPS: *const ::std::os::raw::c_char, + cNumFacturation: *const ::std::os::raw::c_char, + cTypeDonnee: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_ushort; + pub fn SSV_SecuriserFacture( + pcNomRessourcePS: *const ::std::os::raw::c_char, + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + pcCodePorteurPS: *const ::std::os::raw::c_char, + cNologSituation: ::std::os::raw::c_char, + pcNumFact: *const ::std::os::raw::c_char, + pvDataIn: *mut ::std::os::raw::c_void, + szTailleDataIn: usize, + pvDataOut: *mut *mut ::std::os::raw::c_void, + pszTailleDataOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + + // Fonctions de gestion de configuration (GALSS) + + pub fn SSV_LireConfig( + pZDataOut: *mut *mut ::std::os::raw::c_void, + psTailleDataOut: *mut usize, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_LireDateLecteur( + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + pcDateHeure: *mut ::std::os::raw::c_char, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_MajDateLecteur( + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + pcDateHeure: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_ushort; + + pub fn SSV_ChargerAppli( + pcNomRessourceLecteur: *const ::std::os::raw::c_char, + sNbZDataIn: ::std::os::raw::c_short, + pvTZDataIn: *mut *mut ::std::os::raw::c_void, + psTTailleDataIn: *mut usize, + ) -> ::std::os::raw::c_ushort; + + // Fonctions techniques + + // La fonction Initialiser Librairie a pour objet de charger et d’initialiser dans la mémoire du système : + // - dans le cas où le GALSS est installé sur le poste : + // - la bibliothèque du Gestionnaire d’Accès au Lecteur Santé Social (GALSS), + // - qui charge la bibliothèque du Protocole Santé Social (PSS), + // - la configuration du poste de travail à l’aide du fichier galssinf, + // - les variables globales communes aux différents Services SESAM-Vitale, + // - les fichiers de tables et scripts des répertoires par défaut. + // Cette fonction accède au référentiel électronique en utilisant le chemin complet indiqué dans le fichier sesam.ini. + pub fn SSV_InitLIB2(pcFichierSesam: *const ::std::os::raw::c_char) -> ::std::os::raw::c_ushort; + + // La fonction Terminer a pour objet de décharger de la mémoire du système les éléments + // chargés par la fonction Initialiser Librairie, qui ne sont plus utiles. + pub fn SSV_TermLIB() -> ::std::os::raw::c_ushort; + + /// Fonctions de Tracage + //La fonction Allouer Zone Mémoire a un rôle purement technique : elle permet d’allouer, autrement dit de réserver une zone ou partie de la mémoire du poste de travail pour y écrire les données à passer en entrée d’un Service SESAM-Vitale. + // Cette fonction doit être utilisée pour allouer toutes les zones de mémoire requises en entrée des Services SESAM-Vitale de manière à permettre un diagnostic fiable par le « mode trace » en cas de dysfonctionnement. En effet, son mode d’exécution est susceptible de fournir des informations utiles au « mode trace » lorsqu’il est activé. + pub fn SSV_AllouerZoneMem( + pZDataIn: *mut *mut ::std::os::raw::c_void, + taille: usize, + ) -> ::std::os::raw::c_ushort; + + // La fonction Libérer Zone Mémoire a un rôle purement technique : elle permet de libérer une zone de mémoire du poste de travail précédemment allouée après exploitation des données qu’elle contient. + // Cette fonction doit être utilisée pour libérer toutes les zones de mémoire : + // - celles qui ont été allouées par le progiciel de santé pour fournir les données nécessaires à l’entrée des Services SESAM-Vitale, avant leur appel, celles qui ont été allouées par les Services SESAM-Vitale pour fournir en sortie les données utiles au progiciel de santé qui a fait appel à ces services, + // - de façon à permettre un diagnostic fiable par le mode trace en cas de dysfonctionnement + //En effet, son exécution est susceptible de fournir des informations utiles au « mode trace » lorsqu’il est activé. + pub fn SSV_LibererZoneMem(pZone: *mut ::std::os::raw::c_void); + + // La fonction Initialiser Trace a pour objet de permettre l’activation du « mode trace ». + // Ce mode de fonctionnement est prévu pour permettre à l’assistance technique du GIE + // SESAM-Vitale d’analyser les problèmes de mise en œuvre des Services SESAM-Vitale, + // notamment lorsque une fonction retourne un code d’erreur de valeur hexadécimale supérieure à FF00. + pub fn SSV_InitTrace( + pathConf: *mut ::std::os::raw::c_char, + ModeOuvertureFicherLog: *mut ::std::os::raw::c_char, + ModuleLog: ::std::os::raw::c_ushort, + NiveauLog: ::std::os::raw::c_uchar, + ) -> ::std::os::raw::c_ushort; + +} diff --git a/crates/services-sesam-vitale-sys/src/types/common.rs b/crates/services-sesam-vitale-sys/src/types/common.rs new file mode 100644 index 0000000..08ce7eb --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/types/common.rs @@ -0,0 +1,264 @@ +pub struct Identification { + value: T, + // Key to check the validity of the value + // TODO: implement checking algorithm + key: u8, +} + +pub type Byte = u8; + +pub(crate) 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), +} + +pub(crate) 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, +} + +pub(crate) enum CategorieCarteProfessionnelSante { + Reelle, + Test, + Demonstration, +} + +pub(crate) 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, +} + +pub(crate) enum IdentificationStructure { + NumeroAdeliCabinet(String), + NumeroFINESS(String), + NumeroSIREN(String), + NumeroSIRET(String), + NumeroRPPSCabinet(String), +} + +pub(crate) enum ModeExercice { + LiberalExploitantCommercant, // Libéral, exploitant, commerçant + Salarie, + Remplacant, + Benevole, +} + +pub(crate) enum StatutExercice { + // TAB-Statuts géré par l’ANS il faut trouver la donnee + PLACEHOLDER(u8), +} + +pub(crate) 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, +} + +pub(crate) type IdentificationFacturation = u32; +pub(crate) enum CodeConventionnel { + NonConventionne, + Conventionne, + ConventionneAvecDepassement, + ConventionneAvecHonorairesLibres, +} + +/// Code spécialité ou Code spécialité de l'exécutant +pub(crate) enum CodeSpecialite { + MedecineGenerale, + AnesthesieReanimation, + Cardiologie, + ChirurgieGenerale, + DermatologieEtVenerologie, + Radiologie, + GynecologieObstetrique, + GastroEnterologieEtHepatologie, + MedecineInterne, + NeuroChirurgie, + OtoRhinoLaryngologie, + Pediatrie, + Pneumologie, + Rhumatologie, + Ophtalmologie, + ChirurgieUrologique, + NeuroPsychiatrie, + Stomatologie, + ChirurgienDentiste, + ReanimationMedicale, + SageFemme, + SpecialisteEnMedecineGeneraleAvecDiplome, + SpecialisteEnMedecineGeneraleReconnuParLOrdre, + Infirmier, + Psychologue, + MasseurKinesitherapeute, + PedicurePodologue, + Orthophoniste, + Orthoptiste, + LaboratoireDAnalysesMedicales, + ReeducationReadaptationFonctionnelle, + Neurologie, + Psychiatrie, + Geriatrie, + Nephrologie, + ChirurgieDentaireSpecialiteODF, + AnatomoCytoPathologie, + MedecinBiologiste, + LaboratoirePolyvalent, + LaboratoireDAnatomoCytoPathologique, + ChirurgieOrthopediqueEtTraumatologie, + EndocrinologieEtMetabolisme, + ChirurgieInfantile, + ChirurgieMaxilloFaciale, + ChirurgieMaxilloFacialeEtStomatologie, + ChirurgiePlastiqueReconstructriceEtEsthetique, + ChirurgieThoraciqueEtCardioVasculaire, + ChirurgieVasculaire, + ChirurgieVisceraleEtDigestive, + PharmacieDOfficine, + PharmacieMutualiste, + ChirurgienDentisteSpecialiteCO, + ChirurgienDentisteSpecialiteMBD, + PrestataireDeTypeSociete, + PrestataireArtisan, + PrestataireDeTypeAssociation, + Orthesiste, + Opticien, + Audioprothesiste, + ÉpithesisteOculariste, + PodoOrthesiste, + Orthoprothesiste, + ChirurgieOrale, + GynecologieMedicale, + Hematologie, + MedecineNucleaire, + OncologieMedicale, + OncologieRadiotherapique, + PsychiatrieDeLEnfantEtDeLAdolescent, + Radiotherapie, + Obstetrique, + GenetiqueMedicale, + ObstetriqueEtGynecologieMedicale, + SantePubliqueEtMedecineSociale, + MedecineDesMaladiesInfectieusesEtTropicales, + MedecineLegaleEtExpertisesMedicales, + MedecineDUrgence, + MedecineVasculaire, + Allergologie, + InfirmierExercantEnPratiquesAvancees, // IPA +} + +/// Page 54 dictionnaires des donnees +/// donnees inutilises pour les pharmacies +pub(crate) enum CodeZoneTarifaire {} + +pub(crate) enum CodeZoneIK { + PasIndemniteKilometrique, + IndemnitesKilometriquesPlaine, + IndemnitesKilometriquesMontagne, +} + +pub(crate) enum CodeAgrement { + PasDAgrementRadio, + /// Agrément D ou agrément DDASS + AgrementDDASS, + /// Agrément A, B, C, E et F + AgrementABCEF, + /// Agrément G, H et J + AgrementGHJ, + AgrementK, + AgrementL, + AgrementM, +} diff --git a/crates/services-sesam-vitale-sys/src/types/mod.rs b/crates/services-sesam-vitale-sys/src/types/mod.rs new file mode 100644 index 0000000..6e75369 --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/types/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod types; +pub mod common; +pub mod serialization_types; diff --git a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs new file mode 100644 index 0000000..9b9de2d --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs @@ -0,0 +1,81 @@ +use std::cmp::Ordering; + +use binrw::{BinRead, BinResult}; +use bitreader::{BitReader, BitReaderError}; + +fn parse_memory_zone_size_bitreader(bytes: &[u8; 5]) -> Result { + let mut reader = BitReader::new(bytes); + + let zone_size_bit_count = match reader.read_bool()? { + true => 7, + false => reader.read_u8(7)? * 8, + }; + Ok(reader.read_u32(zone_size_bit_count)?) +} + +#[binrw::parser(reader)] +fn parse_memory_zone_size() -> BinResult { + let mut value: u8 = 0; + + + reader.read_exact(std::slice::from_mut(&mut value)); + + // first bit s 0 <=> value < 128 + match value.cmp(&128) { + Ordering::Less => { + Ok(value.into()) + } + Ordering::Equal | Ordering::Greater => { + let zone_size_encoding_byte_count = value - 128; + let mut buf: Vec = vec![0; zone_size_encoding_byte_count.into()]; + reader.read_exact(&mut buf); + + let mut result = 0u32; + + for (i, &byte) in buf.iter().enumerate() { + result += (byte as u32) << (8 * i); + } + + Ok(result) + } + } +} + +#[derive(BinRead)] +struct MemoryZone { + #[br(parse_with = parse_memory_zone_size)] + memory_size: u32, +} + +enum TypeIdentificationStructure { + NumeroAdeliCabinet, + NumeroFINESS, + NumeroSIREN, + NumeroSIRET, + NumeroRPPSCabinet, +} + +pub enum TypeDIdentificationNationale { + NumeroAdeli, + NumeroAdeliCabinetNumeroEmploye, + NumeroDRASS, + NumeroFINESSNumeroEmploye, + NumeroSIRENNumeroEmploye, + NumeroSIRETNumeroEmploye, + NumeroRPPSCabinetNumeroEmploye, + NumeroRPPS, + /// N° Etudiant Médecin type ADELI sur 9 caractères (information transmise par l’ANS) + NumeroEtudiantMedecin, +} +pub(crate) enum TypeCartePS { + /// 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, +} diff --git a/crates/services-sesam-vitale-sys/src/types/types.rs b/crates/services-sesam-vitale-sys/src/types/types.rs new file mode 100644 index 0000000..7d26301 --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/types/types.rs @@ -0,0 +1,45 @@ +pub(crate) use crate::types::common::IdentificationNationale; + +use super::common::{ + Byte, CategorieCarteProfessionnelSante, CodeAgrement, CodeCivilite, CodeConventionnel, + CodeSpecialite, CodeZoneIK, CodeZoneTarifaire, Identification, IdentificationFacturation, + IdentificationStructure, ModeExercice, SecteurActivite, StatutExercice, + TypeCarteProfessionnelSante, +}; + +pub(crate) 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, +} +struct StructureMedicale { + /// Nom Entreprise + raison_sociale: String, + identification: Identification, +} + +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, + identification_facturation: Identification, + identification_remplacant: Option>, + code_conventionnel: CodeConventionnel, + code_specialite: CodeSpecialite, + code_zone_tarifaire: CodeZoneTarifaire, + code_zone_ik: CodeZoneIK, + code_agrement: CodeAgrement, + habilite_signature_facture: bool, + habilite_signature_lot: bool, +} -- 2.45.2 From 34bdea2269c23af2bfaf6422d25e8159ad75d20d Mon Sep 17 00:00:00 2001 From: theo Date: Sat, 10 Aug 2024 12:10:21 +0200 Subject: [PATCH 04/12] WIP commit to set working base --- crates/services-sesam-vitale-sys/src/lib.rs | 132 ++++++++- .../src/types/serialization_types.rs | 259 +++++++++++++----- 2 files changed, 313 insertions(+), 78 deletions(-) diff --git a/crates/services-sesam-vitale-sys/src/lib.rs b/crates/services-sesam-vitale-sys/src/lib.rs index 0b4546e..8b4323e 100644 --- a/crates/services-sesam-vitale-sys/src/lib.rs +++ b/crates/services-sesam-vitale-sys/src/lib.rs @@ -1,17 +1,123 @@ -mod types; +// to include std in docs, need to remove later +#[doc(inline)] +pub use std; -use types::*; -pub fn add(left: usize, right: usize) -> usize { - left + right +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}; + +//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>, +} + +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)) + } +} +#[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> { + 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)), + } + + std::slice::from_raw_parts(buffer_ptr as *const u8, size) + }; + 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(()) } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +mod tests {} 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 9b9de2d..6f0c7af 100644 --- a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs +++ b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs @@ -1,81 +1,210 @@ -use std::cmp::Ordering; +use core::panic; +use std::io::Cursor; -use binrw::{BinRead, BinResult}; -use bitreader::{BitReader, BitReaderError}; +use binrw::{ + binread, + helpers::{read_u24, write_u24}, + BinRead, BinReaderExt, BinResult, BinWriterExt, Endian, +}; -fn parse_memory_zone_size_bitreader(bytes: &[u8; 5]) -> Result { - let mut reader = BitReader::new(bytes); - - let zone_size_bit_count = match reader.read_bool()? { - true => 7, - false => reader.read_u8(7)? * 8, - }; - Ok(reader.read_u32(zone_size_bit_count)?) -} +const U8_MAX: u32 = u8::MAX as u32; +const U16_MAX: u32 = u16::MAX as u32; +const U24_MAX: u32 = 16_777_215; #[binrw::parser(reader)] -fn parse_memory_zone_size() -> BinResult { - let mut value: u8 = 0; - +fn parse_data_size() -> BinResult { + let first_byte: u8 = reader.read_be()?; - reader.read_exact(std::slice::from_mut(&mut value)); + let first_bit: bool = (first_byte & 0b1000_0000) == 0; + Ok(match first_bit { + // first bit is 0 -> size is encoded in the first byte + true => first_byte as u32, - // first bit s 0 <=> value < 128 - match value.cmp(&128) { - Ordering::Less => { - Ok(value.into()) + // first bit is 1 -> size is encoded on N bytes + // N being encoded by the first byte + false => match first_byte { + 0 => 0, + 1 => reader.read_be::()? as u32, + 2 => reader.read_be::()? as u32, + 3 => read_u24(reader, Endian::Big, ())?, + 4 => reader.read_be::()?, + _ => panic!("Length should not be more than 4 bytes"), + }, + }) +} + +#[binrw::writer(writer)] +fn write_data_size(memory_size: &u32) -> BinResult<()> { + match memory_size { + ..=U8_MAX => writer.write_be(&(*memory_size as u8)), + // Since size is not encodable on a single byte + // We write the length encoding the size first, marking it with a flipped first bit + // Then write the size on the following bytes + ..=U16_MAX => { + let size_encoding_length = 2u8 | 0b1000_0000; + writer.write_be(&size_encoding_length)?; + writer.write_be(&(*memory_size as u16)) } - Ordering::Equal | Ordering::Greater => { - let zone_size_encoding_byte_count = value - 128; - let mut buf: Vec = vec![0; zone_size_encoding_byte_count.into()]; - reader.read_exact(&mut buf); - - let mut result = 0u32; - - for (i, &byte) in buf.iter().enumerate() { - result += (byte as u32) << (8 * i); - } - - Ok(result) + ..=U24_MAX => { + let size_encoding_length = 3u8 | 0b1000_0000; + writer.write_be(&size_encoding_length)?; + write_u24(memory_size, writer, Endian::Big, ()) + } + _ => { + let size_encoding_length = 4u8 | 0b1000_0000; + writer.write_be(&size_encoding_length)?; + writer.write_be(memory_size) } } } -#[derive(BinRead)] -struct MemoryZone { - #[br(parse_with = parse_memory_zone_size)] +// 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 +// +// will probably not be used +#[binread] +pub struct DataBlock{//>> { + 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>, } -enum TypeIdentificationStructure { - NumeroAdeliCabinet, - NumeroFINESS, - NumeroSIREN, - NumeroSIRET, - NumeroRPPSCabinet, +#[binread] +pub struct DataField +where + for<'a> T: BinRead= ()>, +{ + #[br(parse_with = parse_data_size)] + memory_size: u32, + + // using data -> not using the parser fw well, I think we can directly parse to the + // corresponding enum + // + // 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 + #[br(count = memory_size)] + #[br(try_map = |data: Vec| T::read_be(&mut Cursor::new(data)))] + pub value: T, } -pub enum TypeDIdentificationNationale { - NumeroAdeli, - NumeroAdeliCabinetNumeroEmploye, - NumeroDRASS, - NumeroFINESSNumeroEmploye, - NumeroSIRENNumeroEmploye, - NumeroSIRETNumeroEmploye, - NumeroRPPSCabinetNumeroEmploye, - NumeroRPPS, - /// N° Etudiant Médecin type ADELI sur 9 caractères (information transmise par l’ANS) - NumeroEtudiantMedecin, -} -pub(crate) enum TypeCartePS { - /// 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, -} +//// Memory allocation functions +//trait DataMaxSize { +// fn max_size(&self) -> usize; +//} +// +//pub struct Real; +//pub struct Test; +//pub struct Demo; +// +//// Trait for categories +//pub trait Category: 'static { +// const NAME: &'static str; +//} +// +//impl Category for Real { +// const NAME: &'static str = "Real"; +//} +//impl Category for Test { +// const NAME: &'static str = "Test"; +//} +//impl Category for Demo { +// const NAME: &'static str = "Demo"; +//} +// +//// Enum for runtime category representation +//pub enum CategoryType { +// Real(Real), +// Test(Test), +// Demo(Demo), +//} +// +//// Card type with generic category +//#[derive(Debug)] +//pub enum CartePS { +// CPS { +// reader_port: u32, +// _category: std::marker::PhantomData, +// }, +// CPF { +// some_cpf_data: String, +// _category: std::marker::PhantomData, +// }, +// CPE { +// some_cpe_data: bool, +// _category: std::marker::PhantomData, +// }, +//} +// +//// Function that only accepts Real CPS cards +//fn process_real_cps_card(card: CartePS<>) { +// if let CartePS::CPS { reader_port, .. } = card { +// println!( +// "Processing a real CPS card with reader port: {}", +// reader_port +// ); +// } +//} +//fn main() { +// let cps = CartePS::::CPS { +// reader_port: 1, +// _category: std::marker::PhantomData, +// }; +// process_real_cps_card(cps); +//} +//// need to see how to interface enums with binrw +//enum IdentificationStructure { +// NumeroAdeliCabinet, +// NumeroFINESS, +// NumeroSIREN, +// NumeroSIRET, +// NumeroRPPSCabinet, +//} +// +//pub enum TypeDIdentificationNationale { +// NumeroAdeli, +// NumeroAdeliCabinetNumeroEmploye, +// NumeroDRASS, +// NumeroFINESSNumeroEmploye, +// NumeroSIRENNumeroEmploye, +// NumeroSIRETNumeroEmploye, +// NumeroRPPSCabinetNumeroEmploye, +// NumeroRPPS, +// /// N° Etudiant Médecin type ADELI sur 9 caractères (information transmise par l’ANS) +// NumeroEtudiantMedecin, +//} +// +////#[derive(BinRead)] +////#[br(repr = [char;2], map = |[u8;2]| )] +//pub(crate) enum TypeCartePS { +// /// Carte de Professionnel de Santé (CPS) +// // CarteDeProfessionnelSante = ('0', '0'), +// /// Carte de Professionnel de Santé en Formation (CPF) +// // CarteDeProfessionnelSanteEnFormation = ('0', '1'), +// /// Carte de Personnel d'Établissement de Santé (CDE/CPE) +// CarteDePersonnelEtablissementSante, +// /// Carte de Personnel Autorisé (CDA/CPA) +// CarteDePersonnelAutorise, +// /// Carte de Personne Morale +// CarteDePersonneMorale, +//} +// +//impl DataMaxSize for TypeCartePS { +// fn max_size(&self) -> usize { +// 2 +// } +//} -- 2.45.2 From 67b482ff131064e581cd3e4b53673fa7305cfd5a Mon Sep 17 00:00:00 2001 From: theo Date: Wed, 28 Aug 2024 21:44:08 +0200 Subject: [PATCH 05/12] feat: implement deku binary reading --- crates/services-sesam-vitale-sys/Cargo.toml | 5 + .../src/types/mod.rs | 2 - .../src/types/serialization_types.rs | 259 +++++------------- 3 files changed, 69 insertions(+), 197 deletions(-) diff --git a/crates/services-sesam-vitale-sys/Cargo.toml b/crates/services-sesam-vitale-sys/Cargo.toml index 21610d1..c357ee8 100644 --- a/crates/services-sesam-vitale-sys/Cargo.toml +++ b/crates/services-sesam-vitale-sys/Cargo.toml @@ -2,3 +2,8 @@ name = "services-sesam-vitale-sys" version = "0.1.0" edition = "2021" + +[dependencies] +bitvec = "1.0.1" +deku = "0.17.0" +libc = "0.2.155" diff --git a/crates/services-sesam-vitale-sys/src/types/mod.rs b/crates/services-sesam-vitale-sys/src/types/mod.rs index 6e75369..3a27f29 100644 --- a/crates/services-sesam-vitale-sys/src/types/mod.rs +++ b/crates/services-sesam-vitale-sys/src/types/mod.rs @@ -1,3 +1 @@ -pub(crate) mod types; -pub mod common; pub mod serialization_types; 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 6f0c7af..65c560d 100644 --- a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs +++ b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs @@ -1,210 +1,79 @@ -use core::panic; -use std::io::Cursor; +use bitvec::index::BitIdx; +use std::{error::Error, vec::Vec}; -use binrw::{ - binread, - helpers::{read_u24, write_u24}, - BinRead, BinReaderExt, BinResult, BinWriterExt, Endian, +use deku::{ + bitvec::{BitStore, Msb0}, + ctx::ByteSize, + deku_derive, + reader::{Reader, ReaderRet}, + DekuError, DekuReader, }; -const U8_MAX: u32 = u8::MAX as u32; -const U16_MAX: u32 = u16::MAX as u32; -const U24_MAX: u32 = 16_777_215; +#[deku_derive(DekuRead)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[deku(endian = "big")] +pub struct GroupId(u16); -#[binrw::parser(reader)] -fn parse_data_size() -> BinResult { - let first_byte: u8 = reader.read_be()?; - - let first_bit: bool = (first_byte & 0b1000_0000) == 0; - Ok(match first_bit { - // first bit is 0 -> size is encoded in the first byte - true => first_byte as u32, - - // first bit is 1 -> size is encoded on N bytes - // N being encoded by the first byte - false => match first_byte { - 0 => 0, - 1 => reader.read_be::()? as u32, - 2 => reader.read_be::()? as u32, - 3 => read_u24(reader, Endian::Big, ())?, - 4 => reader.read_be::()?, - _ => panic!("Length should not be more than 4 bytes"), - }, - }) +trait MapToDekuParseError { + fn map_to_deku_parse_error(self) -> Result; } -#[binrw::writer(writer)] -fn write_data_size(memory_size: &u32) -> BinResult<()> { - match memory_size { - ..=U8_MAX => writer.write_be(&(*memory_size as u8)), - // Since size is not encodable on a single byte - // We write the length encoding the size first, marking it with a flipped first bit - // Then write the size on the following bytes - ..=U16_MAX => { - let size_encoding_length = 2u8 | 0b1000_0000; - writer.write_be(&size_encoding_length)?; - writer.write_be(&(*memory_size as u16)) - } - ..=U24_MAX => { - let size_encoding_length = 3u8 | 0b1000_0000; - writer.write_be(&size_encoding_length)?; - write_u24(memory_size, writer, Endian::Big, ()) - } - _ => { - let size_encoding_length = 4u8 | 0b1000_0000; - writer.write_be(&size_encoding_length)?; - writer.write_be(memory_size) - } +impl MapToDekuParseError for Result { + fn map_to_deku_parse_error(self) -> Result { + self.map_err(|e| DekuError::Parse(e.to_string().into())) } } -// 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 +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct DekuDataField { + #[deku(reader = "read_size(deku::reader)")] + data_size: ByteSize, -// 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 -// -// will probably not be used -#[binread] -pub struct DataBlock{//>> { - 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>, + #[deku(bytes_read = "data_size.0")] + pub data: Vec, } -#[binread] -pub struct DataField -where - for<'a> T: BinRead= ()>, -{ - #[br(parse_with = parse_data_size)] - memory_size: u32, +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct BlockHeader { + pub group_id: GroupId, - // using data -> not using the parser fw well, I think we can directly parse to the - // corresponding enum - // - // 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 - #[br(count = memory_size)] - #[br(try_map = |data: Vec| T::read_be(&mut Cursor::new(data)))] - pub value: T, + #[deku(reader = "read_size(deku::reader)")] + pub data_size: ByteSize, } -//// Memory allocation functions -//trait DataMaxSize { -// fn max_size(&self) -> usize; -//} -// -//pub struct Real; -//pub struct Test; -//pub struct Demo; -// -//// Trait for categories -//pub trait Category: 'static { -// const NAME: &'static str; -//} -// -//impl Category for Real { -// const NAME: &'static str = "Real"; -//} -//impl Category for Test { -// const NAME: &'static str = "Test"; -//} -//impl Category for Demo { -// const NAME: &'static str = "Demo"; -//} -// -//// Enum for runtime category representation -//pub enum CategoryType { -// Real(Real), -// Test(Test), -// Demo(Demo), -//} -// -//// Card type with generic category -//#[derive(Debug)] -//pub enum CartePS { -// CPS { -// reader_port: u32, -// _category: std::marker::PhantomData, -// }, -// CPF { -// some_cpf_data: String, -// _category: std::marker::PhantomData, -// }, -// CPE { -// some_cpe_data: bool, -// _category: std::marker::PhantomData, -// }, -//} -// -//// Function that only accepts Real CPS cards -//fn process_real_cps_card(card: CartePS<>) { -// if let CartePS::CPS { reader_port, .. } = card { -// println!( -// "Processing a real CPS card with reader port: {}", -// reader_port -// ); -// } -//} -//fn main() { -// let cps = CartePS::::CPS { -// reader_port: 1, -// _category: std::marker::PhantomData, -// }; -// process_real_cps_card(cps); -//} -//// need to see how to interface enums with binrw -//enum IdentificationStructure { -// NumeroAdeliCabinet, -// NumeroFINESS, -// NumeroSIREN, -// NumeroSIRET, -// NumeroRPPSCabinet, -//} -// -//pub enum TypeDIdentificationNationale { -// NumeroAdeli, -// NumeroAdeliCabinetNumeroEmploye, -// NumeroDRASS, -// NumeroFINESSNumeroEmploye, -// NumeroSIRENNumeroEmploye, -// NumeroSIRETNumeroEmploye, -// NumeroRPPSCabinetNumeroEmploye, -// NumeroRPPS, -// /// N° Etudiant Médecin type ADELI sur 9 caractères (information transmise par l’ANS) -// NumeroEtudiantMedecin, -//} -// -////#[derive(BinRead)] -////#[br(repr = [char;2], map = |[u8;2]| )] -//pub(crate) enum TypeCartePS { -// /// Carte de Professionnel de Santé (CPS) -// // CarteDeProfessionnelSante = ('0', '0'), -// /// Carte de Professionnel de Santé en Formation (CPF) -// // CarteDeProfessionnelSanteEnFormation = ('0', '1'), -// /// Carte de Personnel d'Établissement de Santé (CDE/CPE) -// CarteDePersonnelEtablissementSante, -// /// Carte de Personnel Autorisé (CDA/CPA) -// CarteDePersonnelAutorise, -// /// Carte de Personne Morale -// CarteDePersonneMorale, -//} -// -//impl DataMaxSize for TypeCartePS { -// fn max_size(&self) -> usize { -// 2 -// } -//} +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct DataBlock { + pub header: BlockHeader, + + #[deku(bytes_read = "header.data_size.0")] + pub data: Vec, +} + +fn read_size(reader: &mut Reader) -> Result { + let first_byte: u8 = u8::from_reader_with_ctx(reader, ())?; + + let is_length_expanded = first_byte.get_bit::(BitIdx::new(0).map_to_deku_parse_error()?); + + match is_length_expanded { + true => { + let size_of_data_size: ByteSize = ByteSize((first_byte & 0b0111_1111) as usize); + + if size_of_data_size.0 > 4 { + return Err(DekuError::Parse("Size of the length encoding is > 4, this is not normal. Probable parsing error".to_string().into())); + }; + + // maximum size of the buffer is 4, we use the offset to read values less than 4 bytes + let buffer: &mut [u8; 4] = &mut [0; 4]; + let write_offset = 4 - size_of_data_size.0; + + match reader.read_bytes(size_of_data_size.0, &mut buffer[write_offset..])? { + ReaderRet::Bits(_bit_vec) => Err(DekuError::Parse("Got bits when trying to read bytes -> reader is unaligned, this is not normal.".to_string().into())), + ReaderRet::Bytes => Ok(ByteSize(u32::from_be_bytes(*buffer) as usize)), + } + } + false => Ok(ByteSize(first_byte as usize)), + } +} -- 2.45.2 From 4871187726478bffc571e7c77034da3f1a9c47ee Mon Sep 17 00:00:00 2001 From: theo Date: Wed, 28 Aug 2024 21:44:29 +0200 Subject: [PATCH 06/12] chore: suppress dead code warning in bindings --- crates/services-sesam-vitale-sys/src/bindings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/services-sesam-vitale-sys/src/bindings.rs b/crates/services-sesam-vitale-sys/src/bindings.rs index c01f0ea..a9a3792 100644 --- a/crates/services-sesam-vitale-sys/src/bindings.rs +++ b/crates/services-sesam-vitale-sys/src/bindings.rs @@ -1,6 +1,7 @@ #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(dead_code)] extern "C" { // Fonctions de gestion des données -- 2.45.2 From fbc3be564f3bc6a2617048ea9105ed126c9f134b Mon Sep 17 00:00:00 2001 From: theo Date: Wed, 28 Aug 2024 23:34:09 +0200 Subject: [PATCH 07/12] feat: implement parsing to data structures --- .../src/types/serialization_types.rs | 88 +++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) 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 65c560d..e28812d 100644 --- a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs +++ b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs @@ -1,5 +1,5 @@ use bitvec::index::BitIdx; -use std::{error::Error, vec::Vec}; +use std::{error::Error, str::FromStr, vec::Vec}; use deku::{ bitvec::{BitStore, Msb0}, @@ -10,7 +10,7 @@ use deku::{ }; #[deku_derive(DekuRead)] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Debug, Clone, Copy, PartialEq)] #[deku(endian = "big")] pub struct GroupId(u16); @@ -26,7 +26,7 @@ impl MapToDekuParseError for Result { #[deku_derive(DekuRead)] #[derive(Debug, PartialEq)] -pub struct DekuDataField { +pub struct DataField { #[deku(reader = "read_size(deku::reader)")] data_size: ByteSize, @@ -48,8 +48,8 @@ pub struct BlockHeader { pub struct DataBlock { pub header: BlockHeader, - #[deku(bytes_read = "header.data_size.0")] - pub data: Vec, + #[deku(ctx = "header.group_id")] + pub inner: DataGroup, } fn read_size(reader: &mut Reader) -> Result { @@ -77,3 +77,81 @@ fn read_size(reader: &mut Reader) -> Result Ok(ByteSize(first_byte as usize)), } } + +// Using this as the map function asks deku to parse a datafield +// We then use the datafield and convert it to the corresponding value +fn convert_from_data_field(data_field: DataField) -> Result +where + T: FromStr, + T::Err: Error, +{ + let text = String::from_utf8(data_field.data).map_to_deku_parse_error()?; + T::from_str(&text).map_to_deku_parse_error() +} + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SSVVersionNumber(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct GALSSVersionNumber(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct PSSVersionNumber(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct ConfigurationHeader { + pub ssv_version: SSVVersionNumber, + pub galss_version: GALSSVersionNumber, + pub pss_version: PSSVersionNumber, +} + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct PCSCReaderName(#[deku(map = "convert_from_data_field")] String); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct CardType(#[deku(map = "convert_from_data_field")] u8); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct PCSCReader { + pub name: PCSCReaderName, + pub card_type: CardType, +} + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponentID(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponentDescription(#[deku(map = "convert_from_data_field")] String); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponentVersion(#[deku(map = "convert_from_data_field")] String); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponent { + pub id: SESAMVitaleComponentID, + pub description: SESAMVitaleComponentDescription, + pub version: SESAMVitaleComponentVersion, +} + +#[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 = 64)] + SESAMVitaleComponent(SESAMVitaleComponent), +} -- 2.45.2 From 681cc7cb833ee0751a13b9187f101e6d2f2e9696 Mon Sep 17 00:00:00 2001 From: theo Date: Thu, 29 Aug 2024 00:46:38 +0200 Subject: [PATCH 08/12] chore: add comment to indicate bindgen --- crates/services-sesam-vitale-sys/src/bindings.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/services-sesam-vitale-sys/src/bindings.rs b/crates/services-sesam-vitale-sys/src/bindings.rs index a9a3792..36f6d25 100644 --- a/crates/services-sesam-vitale-sys/src/bindings.rs +++ b/crates/services-sesam-vitale-sys/src/bindings.rs @@ -3,6 +3,8 @@ #![allow(non_snake_case)] #![allow(dead_code)] +// Generated using bindgen + extern "C" { // Fonctions de gestion des données -- 2.45.2 From 981a5d34c57f990835adcd5c53acf11d975b7d60 Mon Sep 17 00:00:00 2001 From: theo Date: Thu, 29 Aug 2024 00:47:08 +0200 Subject: [PATCH 09/12] 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) } -- 2.45.2 From ecd84bf242c54f1e0d2f9a12d7cdc8bec9615996 Mon Sep 17 00:00:00 2001 From: theo Date: Thu, 29 Aug 2024 01:15:33 +0200 Subject: [PATCH 10/12] feat: add init and close library --- crates/services-sesam-vitale-sys/src/lib.rs | 27 +++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/crates/services-sesam-vitale-sys/src/lib.rs b/crates/services-sesam-vitale-sys/src/lib.rs index ab31ed1..eb4782c 100644 --- a/crates/services-sesam-vitale-sys/src/lib.rs +++ b/crates/services-sesam-vitale-sys/src/lib.rs @@ -1,8 +1,8 @@ mod bindings; pub mod types; -use bindings::SSV_LireConfig; -use std::{fmt, ptr}; +use bindings::{SSV_InitLIB2, SSV_LireConfig, SSV_TermLIB}; +use std::{ffi::CString, fmt, path::Path, ptr}; use types::serialization_types::{read_from_buffer, Configuration}; #[derive(Debug)] @@ -16,6 +16,29 @@ impl fmt::Display for SesamVitaleError { } } +pub fn init_library(sesam_ini_path: &Path) -> Result<(), SesamVitaleError> { + // TODO: better error handling + let path_str = sesam_ini_path.to_str().unwrap(); + let path_ptr = CString::new(path_str).expect("failed to create cstring"); + + let exit_code: u16 = unsafe { SSV_InitLIB2(path_ptr.as_ptr()) }; + if exit_code != 0 { + let error = SesamVitaleError { code: exit_code }; + return Err(error); + }; + + Ok(()) +} +pub fn close_library() -> Result<(), SesamVitaleError> { + let exit_code: u16 = unsafe { SSV_TermLIB() }; + if exit_code != 0 { + let error = SesamVitaleError { code: exit_code }; + return Err(error); + }; + + Ok(()) +} + pub fn read_config() -> Result { let mut buffer_ptr: *mut libc::c_void = ptr::null_mut(); let mut size: libc::size_t = 0; -- 2.45.2 From 080537fe6d18d03c217ff0f1a2bc6db05bb984ac Mon Sep 17 00:00:00 2001 From: theo Date: Wed, 4 Sep 2024 18:45:11 +0200 Subject: [PATCH 11/12] feat: define structure --- crates/services-sesam-vitale-sys/Cargo.toml | 2 + crates/services-sesam-vitale-sys/src/api.rs | 66 ++++ crates/services-sesam-vitale-sys/src/lib.rs | 66 +--- .../src/types/common.rs | 370 ++++++------------ .../src/types/configuration.rs | 137 +++++++ .../src/types/mod.rs | 4 +- .../src/types/serialization_types.rs | 248 ------------ .../src/types/types.rs | 45 --- 8 files changed, 334 insertions(+), 604 deletions(-) create mode 100644 crates/services-sesam-vitale-sys/src/api.rs create mode 100644 crates/services-sesam-vitale-sys/src/types/configuration.rs delete mode 100644 crates/services-sesam-vitale-sys/src/types/serialization_types.rs delete mode 100644 crates/services-sesam-vitale-sys/src/types/types.rs diff --git a/crates/services-sesam-vitale-sys/Cargo.toml b/crates/services-sesam-vitale-sys/Cargo.toml index c357ee8..674084d 100644 --- a/crates/services-sesam-vitale-sys/Cargo.toml +++ b/crates/services-sesam-vitale-sys/Cargo.toml @@ -2,8 +2,10 @@ name = "services-sesam-vitale-sys" version = "0.1.0" edition = "2021" +#links= "ssvlux64" [dependencies] bitvec = "1.0.1" deku = "0.17.0" libc = "0.2.155" +thiserror = "1.0.63" diff --git a/crates/services-sesam-vitale-sys/src/api.rs b/crates/services-sesam-vitale-sys/src/api.rs new file mode 100644 index 0000000..59d0e7d --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/api.rs @@ -0,0 +1,66 @@ +use thiserror::Error; +use std::{ffi::CString, fmt, path::Path, ptr}; + +use crate::{bindings::{SSV_InitLIB2, SSV_TermLIB}, types::{common::read_from_buffer, configuration::Configuration}}; + +#[derive(Error, Debug)] +pub struct SesamVitaleError { + code: u16, +} + +impl fmt::Display for SesamVitaleError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Got error code {} from SSV_LireConfig", self.code) + } +} + +pub fn init_library(sesam_ini_path: &Path) -> Result<(), SesamVitaleError> { + // TODO: better error handling + let path_str = sesam_ini_path.to_str().unwrap(); + let path_ptr = CString::new(path_str).expect("failed to create cstring"); + + let exit_code: u16 = unsafe { SSV_InitLIB2(path_ptr.as_ptr()) }; + if exit_code != 0 { + let error = SesamVitaleError { code: exit_code }; + return Err(error); + }; + + Ok(()) +} +pub fn close_library() -> Result<(), SesamVitaleError> { + let exit_code: u16 = unsafe { SSV_TermLIB() }; + if exit_code != 0 { + let error = SesamVitaleError { code: exit_code }; + return Err(error); + }; + + Ok(()) +} + +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_ptr_ptr: *mut *mut libc::c_void = &mut buffer_ptr; + let size_ptr: *mut libc::size_t = &mut 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) }; + + Ok(configuration) +} + diff --git a/crates/services-sesam-vitale-sys/src/lib.rs b/crates/services-sesam-vitale-sys/src/lib.rs index eb4782c..20f7fd8 100644 --- a/crates/services-sesam-vitale-sys/src/lib.rs +++ b/crates/services-sesam-vitale-sys/src/lib.rs @@ -1,70 +1,6 @@ +pub mod api; mod bindings; pub mod types; -use bindings::{SSV_InitLIB2, SSV_LireConfig, SSV_TermLIB}; -use std::{ffi::CString, fmt, path::Path, ptr}; -use types::serialization_types::{read_from_buffer, Configuration}; - -#[derive(Debug)] -pub struct SesamVitaleError { - code: u16, -} - -impl fmt::Display for SesamVitaleError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Got error code {} from SSV_LireConfig", self.code) - } -} - -pub fn init_library(sesam_ini_path: &Path) -> Result<(), SesamVitaleError> { - // TODO: better error handling - let path_str = sesam_ini_path.to_str().unwrap(); - let path_ptr = CString::new(path_str).expect("failed to create cstring"); - - let exit_code: u16 = unsafe { SSV_InitLIB2(path_ptr.as_ptr()) }; - if exit_code != 0 { - let error = SesamVitaleError { code: exit_code }; - return Err(error); - }; - - Ok(()) -} -pub fn close_library() -> Result<(), SesamVitaleError> { - let exit_code: u16 = unsafe { SSV_TermLIB() }; - if exit_code != 0 { - let error = SesamVitaleError { code: exit_code }; - return Err(error); - }; - - Ok(()) -} - -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_ptr_ptr: *mut *mut libc::c_void = &mut buffer_ptr; - let size_ptr: *mut libc::size_t = &mut 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) }; - - Ok(configuration) -} - #[cfg(test)] mod tests {} diff --git a/crates/services-sesam-vitale-sys/src/types/common.rs b/crates/services-sesam-vitale-sys/src/types/common.rs index 08ce7eb..11ca26a 100644 --- a/crates/services-sesam-vitale-sys/src/types/common.rs +++ b/crates/services-sesam-vitale-sys/src/types/common.rs @@ -1,264 +1,144 @@ -pub struct Identification { - value: T, - // Key to check the validity of the value - // TODO: implement checking algorithm - key: u8, +use crate::types::configuration::{ + ConfigurationHeader, PCSCReader, ReaderConfiguration, SESAMVitaleComponent, +}; + +use std::{error::Error, str::FromStr}; + +use bitvec::index::BitIdx; +use deku::{ + bitvec::{BitStore, Msb0}, + ctx::ByteSize, + deku_derive, + reader::{Reader, ReaderRet}, + DekuContainerRead, DekuError, DekuReader, +}; + +#[deku_derive(DekuRead)] +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct NumericString(#[deku(map = "convert_from_data_field")] String); + +#[deku_derive(DekuRead)] +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct AlphaNumericString(#[deku(map = "convert_from_data_field")] String); + +#[deku_derive(DekuRead)] +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct BinaryData(#[deku(map = "extract_from_data_field")] Vec); + +#[deku_derive(DekuRead)] +#[derive(Debug, Clone, Copy, PartialEq)] +#[deku(endian = "big")] +pub(crate) struct GroupId(u16); + +trait MapToDekuParseError { + fn map_to_deku_parse_error(self) -> Result; } -pub type Byte = u8; - -pub(crate) 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), +impl MapToDekuParseError for Result { + fn map_to_deku_parse_error(self) -> Result { + self.map_err(|e| DekuError::Parse(e.to_string().into())) + } } -pub(crate) 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, +fn read_size(reader: &mut Reader) -> Result { + let first_byte: u8 = u8::from_reader_with_ctx(reader, ())?; + + let is_length_expanded = first_byte.get_bit::(BitIdx::new(0).map_to_deku_parse_error()?); + + match is_length_expanded { + true => { + let size_of_data_size: ByteSize = ByteSize((first_byte & 0b0111_1111) as usize); + + if size_of_data_size.0 > 4 { + return Err(DekuError::Parse("Size of the length encoding is > 4, this is not normal. Probable parsing error".to_string().into())); + }; + + // maximum size of the buffer is 4, we use the offset to read values less than 4 bytes + let buffer: &mut [u8; 4] = &mut [0; 4]; + let write_offset = 4 - size_of_data_size.0; + + match reader.read_bytes(size_of_data_size.0, &mut buffer[write_offset..])? { + ReaderRet::Bits(_bit_vec) => Err(DekuError::Parse("Got bits when trying to read bytes -> reader is unaligned, this is not normal.".to_string().into())), + ReaderRet::Bytes => Ok(ByteSize(u32::from_be_bytes(*buffer) as usize)), + } + } + false => Ok(ByteSize(first_byte as usize)), + } } -pub(crate) enum CategorieCarteProfessionnelSante { - Reelle, - Test, - Demonstration, +// Using this as the map function asks deku to parse a datafield +// We then use the datafield and convert it to the corresponding value +pub(super) fn convert_from_data_field(data_field: DataField) -> Result +where + T: FromStr, + T::Err: Error, +{ + let text = String::from_utf8(data_field.data).map_to_deku_parse_error()?; + T::from_str(&text).map_to_deku_parse_error() } -pub(crate) 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, +pub(crate) fn extract_from_data_field(data_field: DataField) -> Result, DekuError> { + Ok(data_field.data) } -pub(crate) enum IdentificationStructure { - NumeroAdeliCabinet(String), - NumeroFINESS(String), - NumeroSIREN(String), - NumeroSIRET(String), - NumeroRPPSCabinet(String), +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub(crate) struct DataField { + #[deku(reader = "read_size(deku::reader)")] + pub(crate) data_size: ByteSize, + + #[deku(bytes_read = "data_size.0")] + pub(crate) data: Vec, } -pub(crate) enum ModeExercice { - LiberalExploitantCommercant, // Libéral, exploitant, commerçant - Salarie, - Remplacant, - Benevole, +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub(crate) struct BlockHeader { + pub(crate) group_id: GroupId, + + #[deku(reader = "read_size(deku::reader)")] + pub(crate) data_size: ByteSize, } -pub(crate) enum StatutExercice { - // TAB-Statuts géré par l’ANS il faut trouver la donnee - PLACEHOLDER(u8), +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub(crate) struct DataBlock { + pub(crate) header: BlockHeader, + + #[deku(ctx = "header.group_id")] + pub(crate) inner: DataGroup, } -pub(crate) 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, +#[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 = 61)] + ReaderConfiguration(ReaderConfiguration), + #[deku(id = 64)] + SESAMVitaleComponent(SESAMVitaleComponent), + #[deku(id = 67)] + PCSCReader(PCSCReader), } +pub(crate) fn read_from_buffer(buffer: &[u8]) -> Result +where + T: TryFrom>, +{ + let mut data_blocks: Vec = Vec::new(); + let mut offset = 0; -pub(crate) type IdentificationFacturation = u32; -pub(crate) enum CodeConventionnel { - NonConventionne, - Conventionne, - ConventionneAvecDepassement, - ConventionneAvecHonorairesLibres, -} - -/// Code spécialité ou Code spécialité de l'exécutant -pub(crate) enum CodeSpecialite { - MedecineGenerale, - AnesthesieReanimation, - Cardiologie, - ChirurgieGenerale, - DermatologieEtVenerologie, - Radiologie, - GynecologieObstetrique, - GastroEnterologieEtHepatologie, - MedecineInterne, - NeuroChirurgie, - OtoRhinoLaryngologie, - Pediatrie, - Pneumologie, - Rhumatologie, - Ophtalmologie, - ChirurgieUrologique, - NeuroPsychiatrie, - Stomatologie, - ChirurgienDentiste, - ReanimationMedicale, - SageFemme, - SpecialisteEnMedecineGeneraleAvecDiplome, - SpecialisteEnMedecineGeneraleReconnuParLOrdre, - Infirmier, - Psychologue, - MasseurKinesitherapeute, - PedicurePodologue, - Orthophoniste, - Orthoptiste, - LaboratoireDAnalysesMedicales, - ReeducationReadaptationFonctionnelle, - Neurologie, - Psychiatrie, - Geriatrie, - Nephrologie, - ChirurgieDentaireSpecialiteODF, - AnatomoCytoPathologie, - MedecinBiologiste, - LaboratoirePolyvalent, - LaboratoireDAnatomoCytoPathologique, - ChirurgieOrthopediqueEtTraumatologie, - EndocrinologieEtMetabolisme, - ChirurgieInfantile, - ChirurgieMaxilloFaciale, - ChirurgieMaxilloFacialeEtStomatologie, - ChirurgiePlastiqueReconstructriceEtEsthetique, - ChirurgieThoraciqueEtCardioVasculaire, - ChirurgieVasculaire, - ChirurgieVisceraleEtDigestive, - PharmacieDOfficine, - PharmacieMutualiste, - ChirurgienDentisteSpecialiteCO, - ChirurgienDentisteSpecialiteMBD, - PrestataireDeTypeSociete, - PrestataireArtisan, - PrestataireDeTypeAssociation, - Orthesiste, - Opticien, - Audioprothesiste, - ÉpithesisteOculariste, - PodoOrthesiste, - Orthoprothesiste, - ChirurgieOrale, - GynecologieMedicale, - Hematologie, - MedecineNucleaire, - OncologieMedicale, - OncologieRadiotherapique, - PsychiatrieDeLEnfantEtDeLAdolescent, - Radiotherapie, - Obstetrique, - GenetiqueMedicale, - ObstetriqueEtGynecologieMedicale, - SantePubliqueEtMedecineSociale, - MedecineDesMaladiesInfectieusesEtTropicales, - MedecineLegaleEtExpertisesMedicales, - MedecineDUrgence, - MedecineVasculaire, - Allergologie, - InfirmierExercantEnPratiquesAvancees, // IPA -} - -/// Page 54 dictionnaires des donnees -/// donnees inutilises pour les pharmacies -pub(crate) enum CodeZoneTarifaire {} - -pub(crate) enum CodeZoneIK { - PasIndemniteKilometrique, - IndemnitesKilometriquesPlaine, - IndemnitesKilometriquesMontagne, -} - -pub(crate) enum CodeAgrement { - PasDAgrementRadio, - /// Agrément D ou agrément DDASS - AgrementDDASS, - /// Agrément A, B, C, E et F - AgrementABCEF, - /// Agrément G, H et J - AgrementGHJ, - AgrementK, - AgrementL, - AgrementM, + 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) } diff --git a/crates/services-sesam-vitale-sys/src/types/configuration.rs b/crates/services-sesam-vitale-sys/src/types/configuration.rs new file mode 100644 index 0000000..185255f --- /dev/null +++ b/crates/services-sesam-vitale-sys/src/types/configuration.rs @@ -0,0 +1,137 @@ +use crate::types::common::DataBlock; +use std::{error::Error, fmt, vec::Vec}; + +use crate::types::common::convert_from_data_field; +use deku::{deku_derive, DekuReader}; + +use super::common::{AlphaNumericString, DataGroup}; + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SSVVersionNumber(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct GALSSVersionNumber(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct PSSVersionNumber(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct ConfigurationHeader { + pub ssv_version: SSVVersionNumber, + pub galss_version: GALSSVersionNumber, + pub pss_version: PSSVersionNumber, +} + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct PCSCReaderName(AlphaNumericString); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct CardType(#[deku(map = "convert_from_data_field")] u8); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct PCSCReader { + pub name: PCSCReaderName, + pub card_type: CardType, +} + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponentID(#[deku(map = "convert_from_data_field")] u16); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponentDescription(AlphaNumericString); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponentVersion(AlphaNumericString); + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct SESAMVitaleComponent { + pub id: SESAMVitaleComponentID, + pub description: SESAMVitaleComponentDescription, + pub version: SESAMVitaleComponentVersion, +} + +#[deku_derive(DekuRead)] +#[derive(Debug, PartialEq)] +pub struct ReaderConfiguration {} + +#[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, + }) + } +} diff --git a/crates/services-sesam-vitale-sys/src/types/mod.rs b/crates/services-sesam-vitale-sys/src/types/mod.rs index 3a27f29..5e3dc6e 100644 --- a/crates/services-sesam-vitale-sys/src/types/mod.rs +++ b/crates/services-sesam-vitale-sys/src/types/mod.rs @@ -1 +1,3 @@ -pub mod serialization_types; +pub mod common; +pub mod configuration; +pub mod droits_vitale; diff --git a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs b/crates/services-sesam-vitale-sys/src/types/serialization_types.rs deleted file mode 100644 index 470f252..0000000 --- a/crates/services-sesam-vitale-sys/src/types/serialization_types.rs +++ /dev/null @@ -1,248 +0,0 @@ -use bitvec::index::BitIdx; -use std::{error::Error, fmt, str::FromStr, vec::Vec}; - -use deku::{ - 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(crate) struct GroupId(u16); - -trait MapToDekuParseError { - fn map_to_deku_parse_error(self) -> Result; -} - -impl MapToDekuParseError for Result { - fn map_to_deku_parse_error(self) -> Result { - self.map_err(|e| DekuError::Parse(e.to_string().into())) - } -} - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub(crate) struct DataField { - #[deku(reader = "read_size(deku::reader)")] - pub(crate) data_size: ByteSize, - - #[deku(bytes_read = "data_size.0")] - pub(crate) data: Vec, -} - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub(crate) struct BlockHeader { - pub(crate) group_id: GroupId, - - #[deku(reader = "read_size(deku::reader)")] - pub(crate) data_size: ByteSize, -} - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub(crate) struct DataBlock { - pub(crate) header: BlockHeader, - - #[deku(ctx = "header.group_id")] - pub(crate) inner: DataGroup, -} - -fn read_size(reader: &mut Reader) -> Result { - let first_byte: u8 = u8::from_reader_with_ctx(reader, ())?; - - let is_length_expanded = first_byte.get_bit::(BitIdx::new(0).map_to_deku_parse_error()?); - - match is_length_expanded { - true => { - let size_of_data_size: ByteSize = ByteSize((first_byte & 0b0111_1111) as usize); - - if size_of_data_size.0 > 4 { - return Err(DekuError::Parse("Size of the length encoding is > 4, this is not normal. Probable parsing error".to_string().into())); - }; - - // maximum size of the buffer is 4, we use the offset to read values less than 4 bytes - let buffer: &mut [u8; 4] = &mut [0; 4]; - let write_offset = 4 - size_of_data_size.0; - - match reader.read_bytes(size_of_data_size.0, &mut buffer[write_offset..])? { - ReaderRet::Bits(_bit_vec) => Err(DekuError::Parse("Got bits when trying to read bytes -> reader is unaligned, this is not normal.".to_string().into())), - ReaderRet::Bytes => Ok(ByteSize(u32::from_be_bytes(*buffer) as usize)), - } - } - false => Ok(ByteSize(first_byte as usize)), - } -} - -// Using this as the map function asks deku to parse a datafield -// We then use the datafield and convert it to the corresponding value -fn convert_from_data_field(data_field: DataField) -> Result -where - T: FromStr, - T::Err: Error, -{ - let text = String::from_utf8(data_field.data).map_to_deku_parse_error()?; - T::from_str(&text).map_to_deku_parse_error() -} - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct SSVVersionNumber(#[deku(map = "convert_from_data_field")] u16); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct GALSSVersionNumber(#[deku(map = "convert_from_data_field")] u16); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct PSSVersionNumber(#[deku(map = "convert_from_data_field")] u16); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct ConfigurationHeader { - pub ssv_version: SSVVersionNumber, - pub galss_version: GALSSVersionNumber, - pub pss_version: PSSVersionNumber, -} - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct PCSCReaderName(#[deku(map = "convert_from_data_field")] String); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct CardType(#[deku(map = "convert_from_data_field")] u8); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct PCSCReader { - pub name: PCSCReaderName, - pub card_type: CardType, -} - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct SESAMVitaleComponentID(#[deku(map = "convert_from_data_field")] u16); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct SESAMVitaleComponentDescription(#[deku(map = "convert_from_data_field")] String); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct SESAMVitaleComponentVersion(#[deku(map = "convert_from_data_field")] String); - -#[deku_derive(DekuRead)] -#[derive(Debug, PartialEq)] -pub struct SESAMVitaleComponent { - pub id: SESAMVitaleComponentID, - pub description: SESAMVitaleComponentDescription, - 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 = 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) -} diff --git a/crates/services-sesam-vitale-sys/src/types/types.rs b/crates/services-sesam-vitale-sys/src/types/types.rs deleted file mode 100644 index 7d26301..0000000 --- a/crates/services-sesam-vitale-sys/src/types/types.rs +++ /dev/null @@ -1,45 +0,0 @@ -pub(crate) use crate::types::common::IdentificationNationale; - -use super::common::{ - Byte, CategorieCarteProfessionnelSante, CodeAgrement, CodeCivilite, CodeConventionnel, - CodeSpecialite, CodeZoneIK, CodeZoneTarifaire, Identification, IdentificationFacturation, - IdentificationStructure, ModeExercice, SecteurActivite, StatutExercice, - TypeCarteProfessionnelSante, -}; - -pub(crate) 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, -} -struct StructureMedicale { - /// Nom Entreprise - raison_sociale: String, - identification: Identification, -} - -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, - identification_facturation: Identification, - identification_remplacant: Option>, - code_conventionnel: CodeConventionnel, - code_specialite: CodeSpecialite, - code_zone_tarifaire: CodeZoneTarifaire, - code_zone_ik: CodeZoneIK, - code_agrement: CodeAgrement, - habilite_signature_facture: bool, - habilite_signature_lot: bool, -} -- 2.45.2 From 07eae878553605091efb9055222e6dd814c952ad Mon Sep 17 00:00:00 2001 From: Florian Briand Date: Wed, 11 Sep 2024 20:22:47 +0200 Subject: [PATCH 12/12] WIP --- crates/services-sesam-vitale-sys/Cargo.toml | 1 + crates/services-sesam-vitale-sys/src/api.rs | 44 +++++++++++++++++-- .../src/types/mod.rs | 2 +- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/services-sesam-vitale-sys/Cargo.toml b/crates/services-sesam-vitale-sys/Cargo.toml index 674084d..704a653 100644 --- a/crates/services-sesam-vitale-sys/Cargo.toml +++ b/crates/services-sesam-vitale-sys/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" bitvec = "1.0.1" deku = "0.17.0" libc = "0.2.155" +num_enum = { version = "0.7.3", features = ["complex-expressions"] } thiserror = "1.0.63" diff --git a/crates/services-sesam-vitale-sys/src/api.rs b/crates/services-sesam-vitale-sys/src/api.rs index 59d0e7d..7901f85 100644 --- a/crates/services-sesam-vitale-sys/src/api.rs +++ b/crates/services-sesam-vitale-sys/src/api.rs @@ -1,13 +1,52 @@ -use thiserror::Error; +use deku::{deku_derive, DekuContainerRead, DekuError, DekuReader}; use std::{ffi::CString, fmt, path::Path, ptr}; +use thiserror::Error; -use crate::{bindings::{SSV_InitLIB2, SSV_TermLIB}, types::{common::read_from_buffer, configuration::Configuration}}; +use crate::{ + bindings::{SSV_InitLIB2, SSV_LireConfig, SSV_TermLIB}, + types::{common::read_from_buffer, configuration::Configuration}, +}; +use num_enum::FromPrimitive; #[derive(Error, Debug)] pub struct SesamVitaleError { code: u16, } +#[derive(Debug, Eq, PartialEq, FromPrimitive)] +#[repr(u16)] +enum SSVIntError { + CPSNotInserted = 61441, + + #[num_enum(catch_all)] + NotImplemented(u16), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sesam_vitale_error() { + let int_error = SSVIntError::from(61441); + assert_eq!(int_error, SSVIntError::CPSNotInserted); + + let int_error = SSVIntError::from(123); + assert_eq!(int_error, SSVIntError::NotImplemented(123)); + println!("{:?}", int_error); + } +} + +#[derive(Error, Debug)] +enum SSVError { + #[error("Erreur standard de la librairie SSV")] + SSVStandard, + // #[error("Erreur de parsing")] + // Parsing(#[from] ParsingError), + #[error("Erreur inattendue de la librairie SSV (TMP)")] + SSVUnknownTmp, +} + impl fmt::Display for SesamVitaleError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Got error code {} from SSV_LireConfig", self.code) @@ -63,4 +102,3 @@ pub fn read_config() -> Result { Ok(configuration) } - diff --git a/crates/services-sesam-vitale-sys/src/types/mod.rs b/crates/services-sesam-vitale-sys/src/types/mod.rs index 5e3dc6e..6ab3e55 100644 --- a/crates/services-sesam-vitale-sys/src/types/mod.rs +++ b/crates/services-sesam-vitale-sys/src/types/mod.rs @@ -1,3 +1,3 @@ pub mod common; pub mod configuration; -pub mod droits_vitale; +// pub mod droits_vitale; -- 2.45.2