WIP: creation-sys-crate-ssv #69
@ -2,8 +2,10 @@
|
|||||||
name = "services-sesam-vitale-sys"
|
name = "services-sesam-vitale-sys"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
#links= "ssvlux64"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitvec = "1.0.1"
|
bitvec = "1.0.1"
|
||||||
deku = "0.17.0"
|
deku = "0.17.0"
|
||||||
libc = "0.2.155"
|
libc = "0.2.155"
|
||||||
|
thiserror = "1.0.63"
|
||||||
|
66
crates/services-sesam-vitale-sys/src/api.rs
Normal file
66
crates/services-sesam-vitale-sys/src/api.rs
Normal file
@ -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<Configuration, SesamVitaleError> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -1,70 +1,6 @@
|
|||||||
|
pub mod api;
|
||||||
mod bindings;
|
mod bindings;
|
||||||
pub mod types;
|
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<Configuration, SesamVitaleError> {
|
|
||||||
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)]
|
#[cfg(test)]
|
||||||
mod tests {}
|
mod tests {}
|
||||||
|
@ -1,264 +1,144 @@
|
|||||||
pub struct Identification<T> {
|
use crate::types::configuration::{
|
||||||
value: T,
|
ConfigurationHeader, PCSCReader, ReaderConfiguration, SESAMVitaleComponent,
|
||||||
// Key to check the validity of the value
|
};
|
||||||
// TODO: implement checking algorithm
|
|
||||||
key: u8,
|
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<u8>);
|
||||||
|
|
||||||
|
#[deku_derive(DekuRead)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
#[deku(endian = "big")]
|
||||||
|
pub(crate) struct GroupId(u16);
|
||||||
|
|
||||||
|
trait MapToDekuParseError<T> {
|
||||||
|
fn map_to_deku_parse_error(self) -> Result<T, DekuError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Byte = u8;
|
impl<T, E: Error> MapToDekuParseError<T> for Result<T, E> {
|
||||||
|
fn map_to_deku_parse_error(self) -> Result<T, DekuError> {
|
||||||
pub(crate) enum IdentificationNationale {
|
self.map_err(|e| DekuError::Parse(e.to_string().into()))
|
||||||
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 {
|
fn read_size<R: std::io::Read>(reader: &mut Reader<R>) -> Result<ByteSize, DekuError> {
|
||||||
/// Carte de Professionnel de Santé (CPS)
|
let first_byte: u8 = u8::from_reader_with_ctx(reader, ())?;
|
||||||
CarteDeProfessionnelSante,
|
|
||||||
/// Carte de Professionnel de Santé en Formation (CPF)
|
let is_length_expanded = first_byte.get_bit::<Msb0>(BitIdx::new(0).map_to_deku_parse_error()?);
|
||||||
CarteDeProfessionnelSanteEnFormation,
|
|
||||||
/// Carte de Personnel d'Établissement de Santé (CDE/CPE)
|
match is_length_expanded {
|
||||||
CarteDePersonnelEtablissementSante,
|
true => {
|
||||||
/// Carte de Personnel Autorisé (CDA/CPA)
|
let size_of_data_size: ByteSize = ByteSize((first_byte & 0b0111_1111) as usize);
|
||||||
CarteDePersonnelAutorise,
|
|
||||||
/// Carte de Personne Morale
|
if size_of_data_size.0 > 4 {
|
||||||
CarteDePersonneMorale,
|
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 {
|
// Using this as the map function asks deku to parse a datafield
|
||||||
Reelle,
|
// We then use the datafield and convert it to the corresponding value
|
||||||
Test,
|
pub(super) fn convert_from_data_field<T>(data_field: DataField) -> Result<T, DekuError>
|
||||||
Demonstration,
|
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 {
|
pub(crate) fn extract_from_data_field(data_field: DataField) -> Result<Vec<u8>, DekuError> {
|
||||||
Adjudant,
|
Ok(data_field.data)
|
||||||
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 {
|
#[deku_derive(DekuRead)]
|
||||||
NumeroAdeliCabinet(String),
|
#[derive(Debug, PartialEq)]
|
||||||
NumeroFINESS(String),
|
pub(crate) struct DataField {
|
||||||
NumeroSIREN(String),
|
#[deku(reader = "read_size(deku::reader)")]
|
||||||
NumeroSIRET(String),
|
pub(crate) data_size: ByteSize,
|
||||||
NumeroRPPSCabinet(String),
|
|
||||||
|
#[deku(bytes_read = "data_size.0")]
|
||||||
|
pub(crate) data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum ModeExercice {
|
#[deku_derive(DekuRead)]
|
||||||
LiberalExploitantCommercant, // Libéral, exploitant, commerçant
|
#[derive(Debug, PartialEq)]
|
||||||
Salarie,
|
pub(crate) struct BlockHeader {
|
||||||
Remplacant,
|
pub(crate) group_id: GroupId,
|
||||||
Benevole,
|
|
||||||
|
#[deku(reader = "read_size(deku::reader)")]
|
||||||
|
pub(crate) data_size: ByteSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum StatutExercice {
|
#[deku_derive(DekuRead)]
|
||||||
// TAB-Statuts géré par l’ANS il faut trouver la donnee
|
#[derive(Debug, PartialEq)]
|
||||||
PLACEHOLDER(u8),
|
pub(crate) struct DataBlock {
|
||||||
|
pub(crate) header: BlockHeader,
|
||||||
|
|
||||||
|
#[deku(ctx = "header.group_id")]
|
||||||
|
pub(crate) inner: DataGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum SecteurActivite {
|
#[deku_derive(DekuRead)]
|
||||||
EtablissementPublicDeSanté,
|
#[derive(Debug, PartialEq)]
|
||||||
HopitauxMilitaires,
|
#[deku(ctx = "group_id: GroupId", id = "group_id.0")]
|
||||||
EtablissementPrivePSPH, // Participant au Service Public Hospitalier
|
pub enum DataGroup {
|
||||||
EtablissementPriveNonPSPH,
|
#[deku(id = 60)]
|
||||||
DispensaireDeSoins,
|
ConfigurationHeader(ConfigurationHeader),
|
||||||
AutresStructuresDeSoinsRelevantDuServiceDeSanteDesArmees,
|
#[deku(id = 61)]
|
||||||
CabinetIndividuel,
|
ReaderConfiguration(ReaderConfiguration),
|
||||||
CabinetDeGroupe,
|
#[deku(id = 64)]
|
||||||
ExerciceEnSociete,
|
SESAMVitaleComponent(SESAMVitaleComponent),
|
||||||
SecteurPrivePHTempsPlein,
|
#[deku(id = 67)]
|
||||||
TransportSanitaire,
|
PCSCReader(PCSCReader),
|
||||||
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) fn read_from_buffer<T>(buffer: &[u8]) -> Result<T, T::Error>
|
||||||
|
where
|
||||||
|
T: TryFrom<Vec<DataBlock>>,
|
||||||
|
{
|
||||||
|
let mut data_blocks: Vec<DataBlock> = Vec::new();
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
pub(crate) type IdentificationFacturation = u32;
|
let mut remaining_buffer = buffer;
|
||||||
pub(crate) enum CodeConventionnel {
|
|
||||||
NonConventionne,
|
while !remaining_buffer.is_empty() {
|
||||||
Conventionne,
|
// TODO: properly handle errors
|
||||||
ConventionneAvecDepassement,
|
let (rest, data_block) = DataBlock::from_bytes((remaining_buffer, offset)).unwrap();
|
||||||
ConventionneAvecHonorairesLibres,
|
|
||||||
}
|
data_blocks.push(data_block);
|
||||||
|
|
||||||
/// Code spécialité ou Code spécialité de l'exécutant
|
(remaining_buffer, offset) = rest;
|
||||||
pub(crate) enum CodeSpecialite {
|
}
|
||||||
MedecineGenerale,
|
|
||||||
AnesthesieReanimation,
|
T::try_from(data_blocks)
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
|
137
crates/services-sesam-vitale-sys/src/types/configuration.rs
Normal file
137
crates/services-sesam-vitale-sys/src/types/configuration.rs
Normal file
@ -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<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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,3 @@
|
|||||||
pub mod serialization_types;
|
pub mod common;
|
||||||
|
pub mod configuration;
|
||||||
|
pub mod droits_vitale;
|
||||||
|
@ -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<T> {
|
|
||||||
fn map_to_deku_parse_error(self) -> Result<T, DekuError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, E: Error> MapToDekuParseError<T> for Result<T, E> {
|
|
||||||
fn map_to_deku_parse_error(self) -> Result<T, DekuError> {
|
|
||||||
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<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<R: std::io::Read>(reader: &mut Reader<R>) -> Result<ByteSize, DekuError> {
|
|
||||||
let first_byte: u8 = u8::from_reader_with_ctx(reader, ())?;
|
|
||||||
|
|
||||||
let is_length_expanded = first_byte.get_bit::<Msb0>(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<T>(data_field: DataField) -> Result<T, DekuError>
|
|
||||||
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<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)
|
|
||||||
}
|
|
@ -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<IdentificationNationale>,
|
|
||||||
situations_execice: Vec<SituationDExercice>,
|
|
||||||
}
|
|
||||||
struct StructureMedicale {
|
|
||||||
/// Nom Entreprise
|
|
||||||
raison_sociale: String,
|
|
||||||
identification: Identification<IdentificationStructure>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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>,
|
|
||||||
identification_facturation: Identification<IdentificationFacturation>,
|
|
||||||
identification_remplacant: Option<Identification<IdentificationNationale>>,
|
|
||||||
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,
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user