Compare commits

..

No commits in common. "83aef34750008d35bc3aa1bb010b86d8e9b7abdd" and "e9ef6cbb4b0fb1e592d8d318189e7c428660c1d5" have entirely different histories.

5 changed files with 81 additions and 135 deletions

View File

@ -2,6 +2,13 @@ use libc::{c_void, size_t};
use std::ffi::CString; use std::ffi::CString;
use std::ptr; use std::ptr;
/*
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
*/
use crate::libssv::SSV_LireCartePS; use crate::libssv::SSV_LireCartePS;
use crate::ssv_memory::{decode_ssv_memory, Block}; use crate::ssv_memory::{decode_ssv_memory, Block};
@ -11,10 +18,6 @@ pub struct CartePS {
situations: Vec<SituationPS>, 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)] #[derive(Debug, Default)]
struct TitulairePS { struct TitulairePS {
type_de_carte_ps: String, // CN type_de_carte_ps: String, // CN
@ -52,7 +55,7 @@ struct SituationPS {
habilitation_à_signer_un_lot: String, habilitation_à_signer_un_lot: String,
} }
pub fn lire_carte(code_pin: &str, lecteur: &str) -> Result<CartePS, String> { pub fn lire_carte(code_pin: &str, lecteur: &str) -> CartePS {
let resource_ps = CString::new(lecteur).expect("CString::new failed"); let resource_ps = CString::new(lecteur).expect("CString::new failed");
let resource_reader = CString::new("").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 card_number = CString::new(code_pin).expect("CString::new failed");
@ -79,11 +82,11 @@ pub fn lire_carte(code_pin: &str, lecteur: &str) -> Result<CartePS, String> {
decode_carte_ps(groups) decode_carte_ps(groups)
} }
fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> { fn decode_carte_ps(groups: Vec<Block>) -> CartePS {
let mut carte_ps = CartePS::default(); let mut carte_ps = CartePS::default();
for group in groups { for group in groups {
for field in group.content { for (field_index, field) in group.content.iter().enumerate() {
match (group.id, field.id) { match (group.id, field_index + 1) {
(1, 1) => { (1, 1) => {
carte_ps.titulaire.type_de_carte_ps = carte_ps.titulaire.type_de_carte_ps =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
@ -116,7 +119,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
let byte = field.content[0]; let byte = field.content[0];
carte_ps.titulaire.categorie_carte = byte as char; carte_ps.titulaire.categorie_carte = byte as char;
} }
(2..=16, 1) => { (2, 1) => {
carte_ps.situations.push(SituationPS::default()); carte_ps.situations.push(SituationPS::default());
carte_ps carte_ps
.situations .situations
@ -124,19 +127,19 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.unwrap() .unwrap()
.numero_logique_de_la_situation_de_facturation_du_ps = field.content[0]; .numero_logique_de_la_situation_de_facturation_du_ps = field.content[0];
} }
(2..=16, 2) => { (2, 2) => {
carte_ps.situations.last_mut().unwrap().mode_d_exercice = carte_ps.situations.last_mut().unwrap().mode_d_exercice =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 3) => { (2, 3) => {
carte_ps.situations.last_mut().unwrap().statut_d_exercice = carte_ps.situations.last_mut().unwrap().statut_d_exercice =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 4) => { (2, 4) => {
carte_ps.situations.last_mut().unwrap().secteur_d_activite = carte_ps.situations.last_mut().unwrap().secteur_d_activite =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 5) => { (2, 5) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -144,7 +147,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.type_d_identification_structure = .type_d_identification_structure =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 6) => { (2, 6) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -152,7 +155,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.numero_d_identification_structure = .numero_d_identification_structure =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 7) => { (2, 7) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -160,7 +163,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.cle_du_numero_d_identification_structure = .cle_du_numero_d_identification_structure =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 8) => { (2, 8) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -168,7 +171,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.raison_sociale_structure = .raison_sociale_structure =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 9) => { (2, 9) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -176,7 +179,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.numero_d_identification_de_facturation_du_ps = .numero_d_identification_de_facturation_du_ps =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 10) => { (2, 10) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -184,7 +187,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.cle_du_numero_d_identification_de_facturation_du_ps = .cle_du_numero_d_identification_de_facturation_du_ps =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 11) => { (2, 11) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -192,7 +195,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.numero_d_identification_du_ps_remplaçant = .numero_d_identification_du_ps_remplaçant =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 12) => { (2, 12) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -200,35 +203,35 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.cle_du_numero_d_identification_du_ps_remplaçant = .cle_du_numero_d_identification_du_ps_remplaçant =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 13) => { (2, 13) => {
carte_ps.situations.last_mut().unwrap().code_conventionnel = carte_ps.situations.last_mut().unwrap().code_conventionnel =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 14) => { (2, 14) => {
carte_ps.situations.last_mut().unwrap().code_specialite = carte_ps.situations.last_mut().unwrap().code_specialite =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 15) => { (2, 15) => {
carte_ps.situations.last_mut().unwrap().code_zone_tarifaire = carte_ps.situations.last_mut().unwrap().code_zone_tarifaire =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 16) => { (2, 16) => {
carte_ps.situations.last_mut().unwrap().code_zone_ik = carte_ps.situations.last_mut().unwrap().code_zone_ik =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 17) => { (2, 17) => {
carte_ps.situations.last_mut().unwrap().code_agrement_1 = carte_ps.situations.last_mut().unwrap().code_agrement_1 =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 18) => { (2, 18) => {
carte_ps.situations.last_mut().unwrap().code_agrement_2 = carte_ps.situations.last_mut().unwrap().code_agrement_2 =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 19) => { (2, 19) => {
carte_ps.situations.last_mut().unwrap().code_agrement_3 = carte_ps.situations.last_mut().unwrap().code_agrement_3 =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 20) => { (2, 20) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -236,7 +239,7 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.habilitation_à_signer_une_facture = .habilitation_à_signer_une_facture =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
(2..=16, 21) => { (2, 21) => {
carte_ps carte_ps
.situations .situations
.last_mut() .last_mut()
@ -244,16 +247,11 @@ fn decode_carte_ps(groups: Vec<Block>) -> Result<CartePS, String> {
.habilitation_à_signer_un_lot = .habilitation_à_signer_un_lot =
String::from_utf8_lossy(field.content).to_string(); String::from_utf8_lossy(field.content).to_string();
} }
_ => { _ => (),
return Err(format!(
"Unknown (group, field) pair: ({}, {})",
group.id, field.id
))
} }
} }
} }
} carte_ps
Ok(carte_ps)
} }
#[cfg(test)] #[cfg(test)]
@ -263,24 +261,23 @@ mod test_decode_carte_ps {
#[test] #[test]
fn test_francoise_pharmacien0052419() { fn test_francoise_pharmacien0052419() {
let bytes: &[u8] = &[ let bytes: &[u8] = &[
0, 1, 51, // Block 01, Content size 51 0, 1, 51, // 3
1, 48, // Field 01, Content size 1 1, 48, // 2
1, 56, // Field 02, Content size 1 1, 56, // 2
11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, // Field 03, Content size 11 11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, // 12
1, 52, // Field 04, Content size 1 1, 52, // 2
2, 50, 50, // Field 05, Content size 2 2, 50, 50, // 3
17, 80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 17, 80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 57, // 18
57, // Field 06, Content size 17 9, 70, 82, 65, 78, 67, 79, 73, 83, 69, // 10
9, 70, 82, 65, 78, 67, 79, 73, 83, 69, // Field 07, Content size 9 1, 84, // 2
1, 84, // Field 08, Content size 1 // total: 54
0, 2, 83, // Block 02, Content size 83 0, 2, 83, 1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56,
1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24, 1, 56, 24, 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69,
80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49, 50, 50, 49, 57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53,
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, 49, 48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49,
]; ];
let blocks = decode_ssv_memory(bytes, bytes.len()); let blocks = decode_ssv_memory(bytes, bytes.len());
let carte_ps = decode_carte_ps(blocks).unwrap(); let carte_ps = decode_carte_ps(blocks);
assert_eq!(carte_ps.titulaire.type_de_carte_ps, "0"); 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.type_d_identification_nationale, "8");
@ -347,62 +344,4 @@ mod test_decode_carte_ps {
); );
assert_eq!(carte_ps.situations[0].habilitation_à_signer_un_lot, "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!();
}
} }

View File

@ -1,7 +1,9 @@
/// libssv.rs /**
/// * libssv.rs
/// Low level bindings to the SSVLIB dynamic library. *
// TODO : look for creating a dedicated *-sys crate : https://kornel.ski/rust-sys-crate * Low level bindings to the SSVLIB dynamic library.
* TODO : look for creating a dedicated *-sys crate : https://kornel.ski/rust-sys-crate
*/
use libc::{c_char, c_ushort, c_void, size_t}; use libc::{c_char, c_ushort, c_void, size_t};
#[cfg_attr(target_os = "linux", link(name = "ssvlux64"))] #[cfg_attr(target_os = "linux", link(name = "ssvlux64"))]
@ -20,4 +22,4 @@ extern "C" {
TTailleDonneesSortie: *mut size_t, TTailleDonneesSortie: *mut size_t,
) -> c_ushort; ) -> c_ushort;
} }
// TODO : replace void* by Rust struct : https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs /* TODO : replace void* by Rust struct : https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs */

View File

@ -5,4 +5,5 @@ mod ssvlib_demo;
fn main() { fn main() {
ssvlib_demo::demo(); ssvlib_demo::demo();
// XXX
} }

View File

@ -1,27 +1,30 @@
/// # SSV Memory /// # SSV Memory
/// Provide functions to manipulate raw memory from SSV library. /// Provide functions to manipulate raw memory from SSV library.
///
use std::convert::TryFrom; use std::convert::TryFrom;
// TODO : Est-ce qu'on pourrait/devrait définir un type custom pour représenter les tableaux de bytes ?
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
struct ElementSize { struct ElementSize {
pub size: usize, pub size: usize,
pub pad: usize, pub pad: usize,
} }
// TODO : Est-ce qu'on pourrait/devrait définir un type custom pour représenter les tableaux de bytes ?
impl TryFrom<&[u8]> for ElementSize { impl TryFrom<&[u8]> for ElementSize {
type Error = &'static str; type Error = &'static str;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> { fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
/* Longueur:
* - si le bit de poids fort du premier octet est à 0, la longueur est codée sur un octet
* - si le bit de poids fort du premier octet est à 1, les 7 bits de poids faible codent le nombre d'octets utilisés pour coder la longueur
*/
if bytes.is_empty() { if bytes.is_empty() {
return Err("Empty bytes input"); return Err("Empty bytes input");
} }
let mut element_size = ElementSize { size: 0, pad: 1 }; let mut element_size = ElementSize { size: 0, pad: 1 };
// Longueur:
// - si le bit de poids fort du premier octet est à 0, la longueur est codée sur un octet
// - si le bit de poids fort du premier octet est à 1, les 7 bits de poids faible codent le nombre d'octets utilisés pour coder la longueur
if bytes[0] & 0b1000_0000 == 0 { if bytes[0] & 0b1000_0000 == 0 {
// Size coded on 1 byte // Size coded on 1 byte
element_size.size = bytes[0] as usize; element_size.size = bytes[0] as usize;
@ -68,12 +71,9 @@ impl<'a> From<&'a [u8]> for Block<'a> {
let mut field_offset = 0; let mut field_offset = 0;
// While there is still content to read, parse Fields // While there is still content to read, parse Fields
let mut content = Vec::new(); let mut content = Vec::new();
let mut field_id = 1;
while field_offset < block_size { while field_offset < block_size {
let mut field: Field<'a> = raw_content[field_offset..].into(); let field: Field<'a> = raw_content[field_offset..].into();
field.id = field_id;
field_offset += field.size; field_offset += field.size;
field_id += 1;
content.push(field); content.push(field);
} }
Block { Block {
@ -86,7 +86,6 @@ impl<'a> From<&'a [u8]> for Block<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct Field<'a> { pub struct Field<'a> {
pub id: u16,
pub size: usize, pub size: usize,
pub content: &'a [u8], pub content: &'a [u8],
} }
@ -96,7 +95,6 @@ impl<'a> From<&'a [u8]> for Field<'a> {
let ElementSize { size, pad } = bytes.try_into().unwrap(); let ElementSize { size, pad } = bytes.try_into().unwrap();
let contenu = &bytes[pad..pad + size]; let contenu = &bytes[pad..pad + size];
Field { Field {
id: 0,
size: pad + size, size: pad + size,
content: contenu, content: contenu,
} }

View File

@ -1,12 +1,17 @@
/// High level API for the SSV library,
/// based on the low level bindings in libssv.rs.
extern crate dotenv; extern crate dotenv;
use libc::{c_void, size_t}; use libc::{c_void, size_t};
use std::env; /**
use std::ffi::CString; * High level API for the SSV library,
* based on the low level bindings in libssv.rs.
*
*/
use std::path::PathBuf; use std::path::PathBuf;
use std::ffi::CString;
use std::ptr; use std::ptr;
use std::env;
use crate::cps::lire_carte; use crate::cps::lire_carte;
use crate::libssv::{SSV_InitLIB2, SSV_LireConfig}; use crate::libssv::{SSV_InitLIB2, SSV_LireConfig};
@ -39,19 +44,20 @@ fn ssv_lire_config() {
} }
pub fn demo() { pub fn demo() {
// TODO : this is probably not working on release, because I'm not sure it exists a CARGO_MANIFEST_DIR and so it can find the `.env` /*
// Maybe we could use a system standard config path to store a config file * TODO : this is probably not working on release, because I'm not sure it exists a CARGO_MANIFEST_DIR and so it can find the `.env`
* Maybe we could use a system standard config path to store a config file
*/
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let manifest_path = PathBuf::from(manifest_dir); let manifest_path = PathBuf::from(manifest_dir);
dotenv::from_path(manifest_path.join(".env")).ok(); dotenv::from_path(manifest_path.join(".env")).ok();
println!("------- Demo for the SSV library --------"); println!("------- Demo for the SSV library 2 --------");
ssv_init_lib_2(); ssv_init_lib_2();
let code_pin = "1234"; let code_pin = "1234";
let lecteur = "HID Global OMNIKEY 3x21 Smart Card Reader 0"; let lecteur = "HID Global OMNIKEY 3x21 Smart Card Reader 0";
let carte_ps = lire_carte(code_pin, lecteur).unwrap(); let carte_ps = lire_carte(code_pin, lecteur);
println!("CartePS: {:#?}", carte_ps); println!("CartePS: {:#?}", carte_ps);
ssv_lire_config(); ssv_lire_config();