409 lines
16 KiB
Rust
409 lines
16 KiB
Rust
use libc::{c_void, size_t};
|
|
use std::ffi::CString;
|
|
use std::ptr;
|
|
|
|
use crate::libssv::SSV_LireCartePS;
|
|
use crate::ssv_memory::{decode_ssv_memory, Block};
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct CartePS {
|
|
titulaire: TitulairePS,
|
|
situations: Vec<SituationPS>,
|
|
}
|
|
|
|
// 1. CB = Caractères Binaires »
|
|
// 2. CE = Caractères « Etendus » (ISO 8859-1)
|
|
// 3. CA = Caractères Alphanumériques (ASCII?)
|
|
// 4. CN = Caractères Numériques
|
|
#[derive(Debug, Default)]
|
|
struct TitulairePS {
|
|
type_de_carte_ps: String, // CN
|
|
type_d_identification_nationale: String, // CN
|
|
numero_d_identification_nationale: String, // CE - 8 -> 30
|
|
cle_du_numero_d_identification_nationale: String, // CN
|
|
code_civilite: String, // CN
|
|
nom_du_ps: String, // CE - 27
|
|
prenom_du_ps: String, // CE - 27
|
|
categorie_carte: char, // CA
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
struct SituationPS {
|
|
numero_logique_de_la_situation_de_facturation_du_ps: u8,
|
|
mode_d_exercice: String,
|
|
statut_d_exercice: String,
|
|
secteur_d_activite: String,
|
|
type_d_identification_structure: String,
|
|
numero_d_identification_structure: String,
|
|
cle_du_numero_d_identification_structure: String,
|
|
raison_sociale_structure: String,
|
|
numero_d_identification_de_facturation_du_ps: String,
|
|
cle_du_numero_d_identification_de_facturation_du_ps: String,
|
|
numero_d_identification_du_ps_remplaçant: String,
|
|
cle_du_numero_d_identification_du_ps_remplaçant: String,
|
|
code_conventionnel: String,
|
|
code_specialite: String,
|
|
code_zone_tarifaire: String,
|
|
code_zone_ik: String,
|
|
code_agrement_1: String,
|
|
code_agrement_2: String,
|
|
code_agrement_3: String,
|
|
habilitation_à_signer_une_facture: String,
|
|
habilitation_à_signer_un_lot: String,
|
|
}
|
|
|
|
pub fn lire_carte(code_pin: &str, lecteur: &str) -> Result<CartePS, String> {
|
|
let resource_ps = CString::new(lecteur).expect("CString::new failed");
|
|
let resource_reader = CString::new("").expect("CString::new failed");
|
|
let card_number = CString::new(code_pin).expect("CString::new failed");
|
|
|
|
let mut buffer: *mut c_void = ptr::null_mut();
|
|
let mut size: size_t = 0;
|
|
let mut hex_values: &[u8] = &[];
|
|
unsafe {
|
|
let result = SSV_LireCartePS(
|
|
resource_ps.as_ptr(),
|
|
resource_reader.as_ptr(),
|
|
card_number.as_ptr(),
|
|
&mut buffer,
|
|
&mut size,
|
|
);
|
|
println!("SSV_LireCartePS result: {}", result);
|
|
|
|
if !buffer.is_null() {
|
|
hex_values = std::slice::from_raw_parts(buffer as *const u8, size);
|
|
libc::free(buffer);
|
|
}
|
|
}
|
|
let groups = decode_ssv_memory(hex_values, hex_values.len());
|
|
decode_carte_ps(groups)
|
|
}
|
|
|
|
fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
|
|
let mut carte_ps = CartePS::default();
|
|
for group in groups {
|
|
for field in group.content {
|
|
match (group.id, field.id) {
|
|
(1, 1) => {
|
|
carte_ps.titulaire.type_de_carte_ps =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(1, 2) => {
|
|
carte_ps.titulaire.type_d_identification_nationale =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(1, 3) => {
|
|
carte_ps.titulaire.numero_d_identification_nationale =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(1, 4) => {
|
|
carte_ps.titulaire.cle_du_numero_d_identification_nationale =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(1, 5) => {
|
|
carte_ps.titulaire.code_civilite =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(1, 6) => {
|
|
carte_ps.titulaire.nom_du_ps =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(1, 7) => {
|
|
carte_ps.titulaire.prenom_du_ps =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(1, 8) => {
|
|
let byte = field.content[0];
|
|
carte_ps.titulaire.categorie_carte = byte as char;
|
|
}
|
|
(2..=16, 1) => {
|
|
carte_ps.situations.push(SituationPS::default());
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.numero_logique_de_la_situation_de_facturation_du_ps = field.content[0];
|
|
}
|
|
(2..=16, 2) => {
|
|
carte_ps.situations.last_mut().unwrap().mode_d_exercice =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 3) => {
|
|
carte_ps.situations.last_mut().unwrap().statut_d_exercice =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 4) => {
|
|
carte_ps.situations.last_mut().unwrap().secteur_d_activite =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 5) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.type_d_identification_structure =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 6) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.numero_d_identification_structure =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 7) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.cle_du_numero_d_identification_structure =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 8) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.raison_sociale_structure =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 9) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.numero_d_identification_de_facturation_du_ps =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 10) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.cle_du_numero_d_identification_de_facturation_du_ps =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 11) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.numero_d_identification_du_ps_remplaçant =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 12) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.cle_du_numero_d_identification_du_ps_remplaçant =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 13) => {
|
|
carte_ps.situations.last_mut().unwrap().code_conventionnel =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 14) => {
|
|
carte_ps.situations.last_mut().unwrap().code_specialite =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 15) => {
|
|
carte_ps.situations.last_mut().unwrap().code_zone_tarifaire =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 16) => {
|
|
carte_ps.situations.last_mut().unwrap().code_zone_ik =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 17) => {
|
|
carte_ps.situations.last_mut().unwrap().code_agrement_1 =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 18) => {
|
|
carte_ps.situations.last_mut().unwrap().code_agrement_2 =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 19) => {
|
|
carte_ps.situations.last_mut().unwrap().code_agrement_3 =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 20) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.habilitation_à_signer_une_facture =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
(2..=16, 21) => {
|
|
carte_ps
|
|
.situations
|
|
.last_mut()
|
|
.unwrap()
|
|
.habilitation_à_signer_un_lot =
|
|
String::from_utf8_lossy(field.content).to_string();
|
|
}
|
|
_ => {
|
|
return Err(format!(
|
|
"Unknown (group, field) pair: ({}, {})",
|
|
group.id, field.id
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(carte_ps)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test_decode_carte_ps {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_francoise_pharmacien0052419() {
|
|
let bytes: &[u8] = &[
|
|
0, 1, 51, // Block 01, Content size 51
|
|
1, 48, // Field 01, Content size 1
|
|
1, 56, // Field 02, Content size 1
|
|
11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, // Field 03, Content size 11
|
|
1, 52, // Field 04, Content size 1
|
|
2, 50, 50, // Field 05, Content size 2
|
|
17, 80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49,
|
|
57, // Field 06, Content size 17
|
|
9, 70, 82, 65, 78, 67, 79, 73, 83, 69, // Field 07, Content size 9
|
|
1, 84, // Field 08, Content size 1
|
|
0, 2, 83, // Block 02, Content size 83
|
|
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
|
|
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
|
|
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
|
|
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
|
|
];
|
|
let blocks = decode_ssv_memory(bytes, bytes.len());
|
|
let carte_ps = decode_carte_ps(blocks).unwrap();
|
|
|
|
assert_eq!(carte_ps.titulaire.type_de_carte_ps, "0");
|
|
assert_eq!(carte_ps.titulaire.type_d_identification_nationale, "8");
|
|
assert_eq!(
|
|
carte_ps.titulaire.numero_d_identification_nationale,
|
|
"99700524194"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.titulaire.cle_du_numero_d_identification_nationale,
|
|
"4"
|
|
);
|
|
assert_eq!(carte_ps.titulaire.code_civilite, "22");
|
|
assert_eq!(carte_ps.titulaire.nom_du_ps, "PHARMACIEN0052419");
|
|
assert_eq!(carte_ps.titulaire.prenom_du_ps, "FRANCOISE");
|
|
assert_eq!(carte_ps.titulaire.categorie_carte, 'T');
|
|
|
|
assert_eq!(carte_ps.situations.len(), 1);
|
|
assert_eq!(
|
|
carte_ps.situations[0].numero_logique_de_la_situation_de_facturation_du_ps,
|
|
1
|
|
);
|
|
assert_eq!(carte_ps.situations[0].mode_d_exercice, "0");
|
|
assert_eq!(carte_ps.situations[0].statut_d_exercice, "1");
|
|
assert_eq!(carte_ps.situations[0].secteur_d_activite, "86");
|
|
assert_eq!(carte_ps.situations[0].type_d_identification_structure, "1");
|
|
assert_eq!(
|
|
carte_ps.situations[0].numero_d_identification_structure,
|
|
"0B0221958"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[0].cle_du_numero_d_identification_structure,
|
|
"8"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[0].raison_sociale_structure,
|
|
"PHARMACIE DU CENTRE22195"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[0].numero_d_identification_de_facturation_du_ps,
|
|
"00202419"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[0].cle_du_numero_d_identification_de_facturation_du_ps,
|
|
"8"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[0].numero_d_identification_du_ps_remplaçant,
|
|
""
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[0].cle_du_numero_d_identification_du_ps_remplaçant,
|
|
"0"
|
|
);
|
|
assert_eq!(carte_ps.situations[0].code_conventionnel, "1");
|
|
assert_eq!(carte_ps.situations[0].code_specialite, "50");
|
|
assert_eq!(carte_ps.situations[0].code_zone_tarifaire, "10");
|
|
assert_eq!(carte_ps.situations[0].code_zone_ik, "00");
|
|
assert_eq!(carte_ps.situations[0].code_agrement_1, "0");
|
|
assert_eq!(carte_ps.situations[0].code_agrement_2, "0");
|
|
assert_eq!(carte_ps.situations[0].code_agrement_3, "0");
|
|
assert_eq!(
|
|
carte_ps.situations[0].habilitation_à_signer_une_facture,
|
|
"1"
|
|
);
|
|
assert_eq!(carte_ps.situations[0].habilitation_à_signer_un_lot, "1");
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_situations() {
|
|
let bytes: &[u8] = &[
|
|
0, 1, 51, // Block 01, Content size 51
|
|
1, 48, 1, 56, 11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, 1, 52, 2, 50, 50, 17, 80,
|
|
72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 57, 9, 70, 82, 65, 78, 67,
|
|
79, 73, 83, 69, 1, 84, 0, 2, 83, // Block 02, Content size 83
|
|
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
|
|
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
|
|
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
|
|
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 0, 3,
|
|
83, // Block 03, Content size 83
|
|
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
|
|
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
|
|
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
|
|
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 0, 4,
|
|
83, // Block 04, Content size 83
|
|
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24,
|
|
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49,
|
|
57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49,
|
|
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
|
|
];
|
|
let blocks = decode_ssv_memory(bytes, bytes.len());
|
|
let carte_ps = decode_carte_ps(blocks).unwrap();
|
|
|
|
assert_eq!(carte_ps.situations.len(), 3);
|
|
assert_eq!(
|
|
carte_ps.situations[0].raison_sociale_structure,
|
|
"PHARMACIE DU CENTRE22195"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[1].raison_sociale_structure,
|
|
"PHARMACIE DU CENTRE22195"
|
|
);
|
|
assert_eq!(
|
|
carte_ps.situations[2].raison_sociale_structure,
|
|
"PHARMACIE DU CENTRE22195"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_missing_field() {
|
|
todo!();
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_unknown_group_field_pair() {
|
|
todo!();
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_invalid_field_format() {
|
|
todo!();
|
|
}
|
|
}
|