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, } // 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 { 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) -> Result { 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!(); } }