feat: add first draft for crate public api

This commit is contained in:
theo 2024-08-29 00:47:08 +02:00
parent 9539eeb6d0
commit 057f2aaaec
Signed by: theo
SSH Key Fingerprint: SHA256:IbehMhSwpXrGUj7vj9iVvfdwe3g09IL9KLUz0zFzcXU
3 changed files with 135 additions and 296 deletions

View File

@ -1,122 +1,46 @@
// to include std in docs, need to remove later
#[doc(inline)]
pub use std;
mod bindings; mod bindings;
pub mod types; pub mod types;
use std::io::Cursor;
use bindings::SSV_LireConfig; use bindings::SSV_LireConfig;
use binrw::BinRead; use std::{fmt, ptr};
use std::ptr; use types::serialization_types::{read_from_buffer, Configuration};
use types::serialization_types::{DataBlock, DataField};
//pub fn read_carte_professionnel_sante() -> Result<CarteProfessionnelSante, _> { #[derive(Debug)]
// // how to init buffer and give it to library pub struct SesamVitaleError {
// // https://stackoverflow.com/questions/58231215/what-is-proper-rust-way-to-allocate-opaque-buffer-for-external-c-library code: u16,
// //
// // 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 {
//<T> {
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<DataField<T>>,
} }
pub enum SSVError { impl fmt::Display for SesamVitaleError {
Error(u16), fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
} write!(f, "Got error code {} from SSV_LireConfig", self.code)
struct Parseable<T: BinRead>(T);
impl<T> BinRead for Parseable<T>
where
for<'a> T: BinRead<Args<'a> = ()>,
{
type Args<'a> = <DataField<T> as BinRead>::Args<'a>;
fn read_options<R: std::io::prelude::Read + std::io::prelude::Seek>(
reader: &mut R,
endian: binrw::Endian,
args: Self::Args<'_>,
) -> binrw::prelude::BinResult<Self> {
let field = DataField::<T>::read_options(reader, endian, args)?;
Ok(Parseable(field.value))
} }
} }
#[derive(BinRead)]
struct ConfigHeader {
ssv_version: Parseable<u16>,
galss_version: Parseable<u16>,
pss_version: Parseable<u16>,
}
#[derive(BinRead)] pub fn read_config() -> Result<Configuration, SesamVitaleError> {
struct ReaderConfig {
// manufacturer_name: Parseable<String>
}
pub fn read_config() -> Result<(), SSVError> {
let mut buffer_ptr: *mut libc::c_void = ptr::null_mut(); let mut buffer_ptr: *mut libc::c_void = ptr::null_mut();
let mut size: libc::size_t = 0; let mut size: libc::size_t = 0;
let buffer: &[u8] = unsafe { let buffer_ptr_ptr: *mut *mut libc::c_void = &mut buffer_ptr;
match SSV_LireConfig(&mut buffer_ptr, &mut size) { let size_ptr: *mut libc::size_t = &mut size;
0 => (),
error_code => return Err(SSVError::Error(error_code)),
}
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) }; unsafe { libc::free(buffer_ptr) };
println!("Buffer data: {:?}", buffer); Ok(configuration)
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)] #[cfg(test)]

View File

@ -1,176 +0,0 @@
struct Identification<T> {
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 lANS)
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<IdentificationNationale>,
situations_execice: Vec<SituationDExercice>,
}
enum IdentificationStructure {
NumeroAdeliCabinet(String),
NumeroFINESS(String),
NumeroSIREN(String),
NumeroSIRET(String),
NumeroRPPSCabinet(String),
}
struct StructureMedicale {
identification: Identification<IdentificationStructure>,
raison_sociale: String, // Nom Entreprise
}
enum ModeExercice {
LiberalExploitantCommercant, // Libéral, exploitant, commerçant
Salarie,
Remplacant,
Benevole,
}
enum StatutExercice {
// TAB-Statuts géré par lANS 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<ModeExercice>,
statut_exercice: Option<StatutExercice>,
secteur_activite: Option<SecteurActivite>,
structure_d_exercice: Option<StructureMedicale>,
}

View File

@ -1,18 +1,14 @@
use bitvec::index::BitIdx; use bitvec::index::BitIdx;
use std::{error::Error, str::FromStr, vec::Vec}; use std::{error::Error, fmt, str::FromStr, vec::Vec};
use deku::{ use deku::{
bitvec::{BitStore, Msb0}, bitvec::{BitStore, Msb0}, ctx::ByteSize, deku_derive, reader::{Reader, ReaderRet}, DekuContainerRead, DekuError, DekuReader
ctx::ByteSize,
deku_derive,
reader::{Reader, ReaderRet},
DekuError, DekuReader,
}; };
#[deku_derive(DekuRead)] #[deku_derive(DekuRead)]
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
#[deku(endian = "big")] #[deku(endian = "big")]
pub struct GroupId(u16); pub(crate) struct GroupId(u16);
trait MapToDekuParseError<T> { trait MapToDekuParseError<T> {
fn map_to_deku_parse_error(self) -> Result<T, DekuError>; fn map_to_deku_parse_error(self) -> Result<T, DekuError>;
@ -26,30 +22,30 @@ impl<T, E: Error> MapToDekuParseError<T> for Result<T, E> {
#[deku_derive(DekuRead)] #[deku_derive(DekuRead)]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct DataField { pub(crate) struct DataField {
#[deku(reader = "read_size(deku::reader)")] #[deku(reader = "read_size(deku::reader)")]
data_size: ByteSize, pub(crate) data_size: ByteSize,
#[deku(bytes_read = "data_size.0")] #[deku(bytes_read = "data_size.0")]
pub data: Vec<u8>, pub(crate) data: Vec<u8>,
} }
#[deku_derive(DekuRead)] #[deku_derive(DekuRead)]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct BlockHeader { pub(crate) struct BlockHeader {
pub group_id: GroupId, pub(crate) group_id: GroupId,
#[deku(reader = "read_size(deku::reader)")] #[deku(reader = "read_size(deku::reader)")]
pub data_size: ByteSize, pub(crate) data_size: ByteSize,
} }
#[deku_derive(DekuRead)] #[deku_derive(DekuRead)]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct DataBlock { pub(crate) struct DataBlock {
pub header: BlockHeader, pub(crate) header: BlockHeader,
#[deku(ctx = "header.group_id")] #[deku(ctx = "header.group_id")]
pub inner: DataGroup, pub(crate) inner: DataGroup,
} }
fn read_size<R: std::io::Read>(reader: &mut Reader<R>) -> Result<ByteSize, DekuError> { fn read_size<R: std::io::Read>(reader: &mut Reader<R>) -> Result<ByteSize, DekuError> {
@ -144,14 +140,109 @@ pub struct SESAMVitaleComponent {
pub version: SESAMVitaleComponentVersion, pub version: SESAMVitaleComponentVersion,
} }
#[deku_derive(DekuRead)]
#[derive(Debug, PartialEq)]
pub struct ReaderConfiguration {}
#[deku_derive(DekuRead)] #[deku_derive(DekuRead)]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[deku(ctx = "group_id: GroupId", id = "group_id.0")] #[deku(ctx = "group_id: GroupId", id = "group_id.0")]
pub enum DataGroup { pub enum DataGroup {
#[deku(id = 60)] #[deku(id = 60)]
ConfigurationHeader(ConfigurationHeader), ConfigurationHeader(ConfigurationHeader),
#[deku(id = 67)] #[deku(id = 61)]
PCSCReader(PCSCReader), ReaderConfiguration(ReaderConfiguration),
#[deku(id = 64)] #[deku(id = 64)]
SESAMVitaleComponent(SESAMVitaleComponent), 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<ReaderConfiguration>,
pub sesam_vitale_components: Vec<SESAMVitaleComponent>,
pub pcsc_readers: Vec<PCSCReader>,
}
impl TryFrom<Vec<DataBlock>> for Configuration {
type Error = ConfigurationError;
fn try_from(data_blocks: Vec<DataBlock>) -> Result<Self, Self::Error> {
let mut configuration_header: Option<ConfigurationHeader> = None;
let mut reader_configurations: Vec<ReaderConfiguration> = Vec::new();
let mut sesam_vitale_components: Vec<SESAMVitaleComponent> = Vec::new();
let mut pcsc_readers: Vec<PCSCReader> = 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<T: TryFrom<Vec<DataBlock>>>(buffer: &[u8]) -> Result<T, T::Error>{
let mut data_blocks: Vec<DataBlock> = 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)
} }