31 Commits

Author SHA1 Message Date
b61634fea9 2024-08-15 17:19:40 +02:00
6b6658f56d 2024-08-15 14:36:55 +02:00
d56f7a2249 2024-08-14 15:48:24 +02:00
2b45f5aa7e 2024-08-14 15:33:56 +02:00
a3fef1c38c 2024-08-14 13:50:09 +02:00
a64876cfa0 init 2024-08-14 13:18:04 +02:00
5269dd7789 Merge pull request 'refactor: Used askama_axum::Template' (#51) from askama_axum_template into main
### Détails

Changement de `askama::Template` à `askama_axum::Template`.

### Pourquoi ?

Pour supprimer les `into_response()` et ainsi réduire les imports

### Documentation

https://djc.github.io/askama/integrations.html#axum-integration

Reviewed-on: P4Pillon/Krys4lide#51
Reviewed-by: florian_briand <florian.briand@digital-engine.info>
2024-08-10 17:51:51 +02:00
c3f97564d6 refactor: Used askama_axum::Template
docs: https://djc.github.io/askama/integrations.html#axum-integration
2024-08-10 16:59:43 +02:00
69a2d11501 Merge pull request 'Configurer le re-build automatique de l'app front lors de changements' (#47) from html_auto_reload into main
### Détails

Nous avons plusieurs besoins :
- reconstruire le serveur lorsque des fichiers _Rust_ ont été modifiés
- recharger les fichiers HTML lors d'un changement directement dans le navigateur

Le fichier `.ignore` permet d'indiquer à `systemfd` de ne pas surveiller l'état de ses fichiers.

Actuellement lors d'une modification, on se retrouve sur la page d'accueil vu que l'url reste toujours la même lors d'un changement de page.

### Pourquoi ?

Pour être plus rapide lors du développement et ainsi ne pas à avoir à relancer les commandes trop régulièrement.

### Documentation

Documentation des librairies
- [auto-reload sur Axum](6bd6556385/examples/auto-reload/README.md)
- [tower_livereload](https://docs.rs/tower-livereload/latest/tower_livereload/)

### Todo

- [x] Documenter un peu plus
- [x] Rendre plus lisible `main.rs`

Fix #44

Reviewed-on: P4Pillon/Krys4lide#47
Reviewed-by: florian_briand <florian.briand@digital-engine.info>
2024-08-09 15:50:54 +02:00
fb201f9d5d refacto: extract livereload layer setup into a function
Co-authored-by: kosssi <github@fafaru.com>
2024-08-09 13:58:12 +02:00
dcb4a7680e refacto: extract TCP Listener building into a dedicated function
Co-authored-by: kosssi <github@fafaru.com>
2024-08-09 12:27:31 +02:00
0c8e417f11 docs: Move documentation on code 2024-08-09 01:41:03 +02:00
73f45442b6 feat: Add dev dependencies with cargo
cargo add cargo-watch --dev --package app
cargo add systemfd --dev --package app
2024-08-09 01:29:59 +02:00
9c57b119ce docs: Ajout de documentation autour de l'auto-reload et du livereload 2024-08-09 00:35:36 +02:00
237bbe789f feat: Add livereload 2024-08-08 22:19:24 +02:00
1ae80c161f feat: Add auto-reload on development environment 2024-08-08 22:18:26 +02:00
668a91941b Merge pull request 'style: Format code with fmt' (#48) from fmt into main
### Détails

`fmt` permet de formater le code Rust, je pense que c'est une bonne chose que l'on utilise l'utilitaire aevc la commande `cargo fmt` nous pourrons mettre en place un test de validation des DAs quand nous aurons une CI avec la commande suivante `cargo fmt --all -- --check`.

### Pourquoi ?

Pour rendre le code plus lisible

### Documentation

https://rust-lang.github.io/rustfmt/

Reviewed-on: P4Pillon/Krys4lide#48
Reviewed-by: theo <theo.lettermann@gmail.com>
2024-08-08 15:08:03 +02:00
0eaf238735 style: Format code with fmt 2024-08-08 15:01:28 +02:00
b10fc30984 Merge pull request 'Ajout d'un système de pages pour la barre de navigation' (#42) from feat/8_implement_main_ui_part2 into main
Cette PR explore une organisation en "pages" pour le rendu des différents "onglets" de la navbar.

Contrairement à la PR précédente (#40), les composants ajoutés viennent de la bibliothèque Flowbite

En plus des pages, cette PR :
- remplace le texte de chargement Chargement... par des skeleton (sorte de placeholder visuels)
- utilise le système HTMX de hx-swap-oob pour changer le titre dynamiquement
- ajoutent les fichiers minifiés de de htmx / alpinejs / flowbite dans les assets, avec leur numéro de version

Reviewed-on: P4Pillon/Krys4lide#42
Reviewed-by: kosssi <simon@p4pillon.org>
Reviewed-by: theo <theo.lettermann@gmail.com>
2024-08-06 21:50:20 +02:00
e8f4c50ad0 refacto: add alpinejs, flowbite and htmx to app assets with explicit versions 2024-08-06 21:30:14 +02:00
e084372b44 feat: add page system behing navbar items and skeletons for loading 2024-08-06 21:19:24 +02:00
898ee32f9a Merge pull request 'Interface - Implémentation d'une première ébauche technique' (#40) from feat/8_implement_main_ui into main
Cette PR met concrètement en place une première interface combinant les technologies front choisies pour le projet.

- Setup de tailwindcss (+ documentation basique sur son usage)
- Implémentation d'un composant "complexe" de barre de navigation
- Gestion du responsive
- Combinaison de Askama (Jinja) + HTMX
- Usage de AlpineJS pour les micro-interactions (affichage des menus)

À suivre, dans la PR #42 : une interface moins "random" et plus orientée vers nos besoins

Contribue à #8

Reviewed-on: P4Pillon/Krys4lide#40
Reviewed-by: kosssi <simon@p4pillon.org>
Reviewed-by: theo <theo.lettermann@gmail.com>
2024-08-06 21:12:31 +02:00
f3495b8fb4 fixup! feat: make Nav and Profile menu dynamic 2024-08-06 21:11:04 +02:00
06e03011d8 fixup! feat: Setup Tailwind CSS 2024-08-06 21:11:04 +02:00
78bf81c301 feat: make Nav and Profile menu dynamic 2024-08-06 21:11:04 +02:00
aba6c101cb feat: replace vanilla JS by AlpineJS 2024-08-06 21:11:04 +02:00
23f85c5e92 feat: add navbar layout with some JS controls (with Alpine.js) 2024-08-06 21:11:04 +02:00
e057889403 refacto: nest axum templates routes 2024-08-06 21:11:04 +02:00
4a27dacd8e feat: Setup Tailwind CSS 2024-08-06 21:11:04 +02:00
a365e9206f Merge pull request 'docs: Add Tauri version' (#41) from tauri_version into main
### Détails

Spécifie la version de Tauri à installer dans la documentation

### Pourquoi ?

Il faut la bonne version de Tauri pour que ça fonctionne et que l'on soit raccord dans l'équipe

Reviewed-on: P4Pillon/Krys4lide#41
Reviewed-by: florian_briand <florian.briand@digital-engine.info>
2024-08-06 09:39:20 +02:00
4b96e6348e docs: Add Tauri version 2024-08-06 00:15:51 +02:00
33 changed files with 2537 additions and 107 deletions

5
.ignore Normal file
View File

@ -0,0 +1,5 @@
# Ignorer les fichiers dont ne dépent pas la compilation
*.md
tailwind.config.js
*.example
scripts

1487
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ Logiciel de Pharmacie libre et open-source.
La CLI Tauri est nécessaire au lancement du client `desktop`. Elle peut être installée via Cargo :
```bash
cargo install tauri-cli
cargo install tauri-cli --version "^2.0.0-beta"
```
#### Tailwindcss CLI
@ -47,6 +47,19 @@ Si vous souhaitez lancer les composants séparément, les indications de lanceme
- [app](crates/app/README.md)
- [sesam-vitale](crates/sesam-vitale/README.md)
## Rechargement automatique
Pour permettre de développer plus rapidement, il existe une librairie qui recompile automatiquement nos modifications en cours : [`cargo-watch`](https://github.com/watchexec/cargo-watch) permet de relancer une commande `cargo` lorsqu'un fichier est modifié (example: `cargo run` --> `cargo watch -x run`).
Voici la commande pour l'installer dans un _package_ :
```bash
cargo add cargo-watch --dev --package app
```
Le fichier [`.ignore`](./ignore) permet d'ignorer certains fichiers pour éviter de relancer la recompilation inutilement.
⚠️ La librairie n'est pas compatible avec _Windows 7_ et les versions antérieurs de _Windows_.
## Build
Packager le client desktop

View File

@ -7,7 +7,13 @@ edition = "2021"
askama = "0.12.1"
askama_axum = "0.4.0"
axum = "0.7.5"
listenfd = "1.0.1"
notify = "6.1.1"
serde = { version = "1.0.204", features = ["derive"] }
tokio = { version = "1.39.1", features = ["macros", "rt-multi-thread"] }
tower-http = { version = "0.5.2", features = ["fs"] }
tower-livereload = "0.9.3"
[dev-dependencies]
cargo-watch = "8.5.1"
systemfd = "0.4.0"

View File

@ -13,3 +13,23 @@
```bash
cargo run --bin app
```
## Rechargement automatique (_auto-reload_)
Pour le projet `app`, nous utilisons en plus de `cargo-watch` ses librairies :
- [`systemfd`](https://github.com/mitsuhiko/systemfd) permet de redémarrer un serveur sans interrompre les connexions en cours, il transmet le descripteur de fichier du socket à une nouvelle instance du serveur (exemple: `cargo watch -x run` --> `systemfd --no-pid -s http::3000 -- cargo watch -x run`). Si le port est déjà pris il en prendra un autre.
- [`listenfd`](https://github.com/mitsuhiko/listenfd) permet, côté _Rust_, de démarrer un serveur en utilisant des connexions déjà ouvertes.
Pour notre application voici la commande à lancer :
```bash
systemfd --no-pid -s http::3000 -- cargo watch -x 'run --bin app'
```
## Chargement à chaud (_livereload_)
Pour que notre navigateur rafraîchisse automatique notre page lorsque le serveur a été recompilé, nous utilisons la librairie [`tower-livereload`](https://github.com/leotaku/tower-livereload).
A chaque changement, que ça soit sur du code en _Rust_, _HTML_, _CSS_ ou _JS_ alors le navigateur va recharger entièrement la page.
En Rust, il n'existe pas encore d'outil de _Hot Reload_ complet et intégré comme on en trouve dans d'autres environnements de développement web, comme pour _Node.js_.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,8 +3,7 @@ mod templates;
use std::path::Path;
use askama::Template;
use askama_axum::IntoResponse;
use askama_axum::Template;
use axum::http::{StatusCode, Uri};
use tower_http::services::ServeDir;
@ -14,10 +13,10 @@ async fn fallback(uri: Uri) -> (StatusCode, String) {
#[derive(Template)]
#[template(path = "index.html")]
pub struct GetIndexResponse;
pub struct GetIndexTemplate;
async fn root() -> impl IntoResponse {
GetIndexResponse {}.into_response()
async fn root() -> GetIndexTemplate {
GetIndexTemplate {}
}
pub fn get_router(assets_path: &Path) -> axum::Router {

View File

@ -1,15 +1,63 @@
use ::app::get_router;
use axum::body::Body;
use axum::http::Request;
use listenfd::ListenFd;
use notify::Watcher;
use std::env;
use std::path::Path;
use tokio::net::TcpListener;
use tower_livereload::predicate::Predicate;
use tower_livereload::LiveReloadLayer;
/// Nous filtrons les requêtes de `htmx` pour ne pas inclure le script _JS_ qui gère le rechargement
/// Voir https://github.com/leotaku/tower-livereload/pull/3
#[derive(Copy, Clone)]
struct NotHtmxPredicate;
impl<T> Predicate<Request<T>> for NotHtmxPredicate {
fn check(&mut self, req: &Request<T>) -> bool {
!(req.headers().contains_key("hx-request"))
}
}
const DEFAULT_LISTENER: &str = "localhost:3000";
async fn get_tcp_listener() -> TcpListener {
let mut listenfd = ListenFd::from_env();
match listenfd.take_tcp_listener(0).unwrap() {
// if we are given a tcp listener on listen fd 0, we use that one
Some(listener) => {
listener.set_nonblocking(true).unwrap();
TcpListener::from_std(listener).unwrap()
}
// otherwise fall back to local listening
None => TcpListener::bind(DEFAULT_LISTENER).await.unwrap(),
}
}
fn get_livereload_layer(templates_path: &Path) -> LiveReloadLayer<NotHtmxPredicate> {
let livereload = LiveReloadLayer::new();
let reloader = livereload.reloader();
let mut watcher = notify::recommended_watcher(move |_| reloader.reload()).unwrap();
watcher
.watch(templates_path, notify::RecursiveMode::Recursive)
.unwrap();
livereload.request_predicate::<Body, NotHtmxPredicate>(NotHtmxPredicate)
}
#[tokio::main]
async fn main() {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let assets_path = Path::new(&manifest_dir).join("assets");
let router = get_router(assets_path.as_path());
let templates_path = Path::new(&manifest_dir).join("templates");
// TODO: select port based on available port (or ask in CLI)
let listener = tokio::net::TcpListener::bind("localhost:3000").await.unwrap();
let livereload_layer = get_livereload_layer(&templates_path);
let router = get_router(assets_path.as_path()).layer(livereload_layer);
let listener: TcpListener = get_tcp_listener().await;
println!("Listening on: http://{}", listener.local_addr().unwrap());
axum::serve(listener, router).await.unwrap();
// Run the server with the router
axum::serve(listener, router.into_make_service())
.await
.unwrap();
}

View File

@ -1,10 +1,9 @@
use askama::Template;
use askama_axum::IntoResponse;
use askama_axum::Template;
#[derive(Template)]
#[template(path = "pages/cps.html")]
struct CpsResponse;
pub struct CpsTemplate;
pub async fn cps() -> impl IntoResponse {
CpsResponse.into_response()
}
pub async fn cps() -> CpsTemplate {
CpsTemplate
}

View File

@ -1,10 +1,9 @@
use askama::Template;
use askama_axum::IntoResponse;
use askama_axum::Template;
#[derive(Template)]
#[template(path = "pages/home.html")]
struct HomeResponse;
pub struct HomeTemplate;
pub async fn home() -> impl IntoResponse {
HomeResponse.into_response()
}
pub async fn home() -> HomeTemplate {
HomeTemplate
}

View File

@ -7,4 +7,4 @@ pub fn get_routes() -> Router {
Router::new()
.route("/home", routing::get(home::home))
.route("/cps", routing::get(cps::cps))
}
}

View File

@ -1,20 +1,18 @@
use askama::Template;
use askama_axum::IntoResponse;
use askama_axum::Template;
use axum::{routing, Router};
#[derive(Template)]
#[template(path = "hello.html")]
struct HelloResponse {
struct HelloTemplate {
pub name: String,
}
async fn hello() -> impl IntoResponse {
HelloResponse {
async fn hello() -> HelloTemplate {
HelloTemplate {
name: "Theo".to_string(),
}.into_response()
}
}
pub fn get_routes() -> Router {
Router::new()
.route("/", routing::get(hello))
Router::new().route("/", routing::get(hello))
}

View File

@ -1,5 +1,4 @@
use askama::Template;
use askama_axum::IntoResponse;
use askama_axum::Template;
use axum::{extract::Query, routing, Router};
use serde::Deserialize;
@ -16,16 +15,18 @@ struct MenuParameters {
#[derive(Template)]
#[template(path = "layout/nav/nav-menu-items.html")]
struct MenuResponse {
struct MenuTemplate {
mobile: bool,
items: Vec<MenuItem>,
}
impl MenuResponse {
impl MenuTemplate {
fn get_classes(&self, is_current_item: &bool) -> String {
let common_classes = match self.mobile {
true => "block border-l-4 py-2 pl-3 pr-4 text-base font-medium".to_string(),
false => "inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium".to_string(),
false => {
"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium".to_string()
}
};
match (self.mobile, is_current_item) {
(true, true) => common_classes + " border-indigo-500 bg-indigo-50 text-indigo-700",
@ -36,8 +37,8 @@ impl MenuResponse {
}
}
async fn menu(Query(params): Query<MenuParameters>) -> impl IntoResponse {
MenuResponse {
async fn menu(Query(params): Query<MenuParameters>) -> MenuTemplate {
MenuTemplate {
mobile: params.mobile,
items: vec![
MenuItem {
@ -50,11 +51,10 @@ async fn menu(Query(params): Query<MenuParameters>) -> impl IntoResponse {
href: "/pages/cps".to_string(),
current: false,
},
],
}.into_response()
],
}
}
pub fn get_routes() -> Router {
Router::new()
.route("/menu", routing::get(menu))
Router::new().route("/menu", routing::get(menu))
}

View File

@ -1,5 +1,4 @@
use askama::Template;
use askama_axum::IntoResponse;
use askama_axum::Template;
use axum::{extract::Query, routing, Router};
use serde::Deserialize;
@ -16,12 +15,12 @@ struct MenuParameters {
#[derive(Template)]
#[template(path = "layout/nav/profile-menu-items.html")]
struct MenuResponse {
struct MenuTemplate {
mobile: bool,
items: Vec<MenuItem>,
}
impl MenuResponse {
impl MenuTemplate {
fn get_classes(&self, is_current_item: &bool) -> String {
let common_classes = match self.mobile {
true => "block px-4 py-2 text-base font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-800".to_string(),
@ -36,8 +35,8 @@ impl MenuResponse {
}
}
async fn menu(Query(params): Query<MenuParameters>) -> impl IntoResponse {
MenuResponse {
async fn menu(Query(params): Query<MenuParameters>) -> MenuTemplate {
MenuTemplate {
mobile: params.mobile,
items: vec![
MenuItem {
@ -56,10 +55,9 @@ async fn menu(Query(params): Query<MenuParameters>) -> impl IntoResponse {
current: false,
},
],
}.into_response()
}
}
pub fn get_routes() -> Router {
Router::new()
.route("/menu", routing::get(menu))
}
Router::new().route("/menu", routing::get(menu))
}

View File

@ -3,11 +3,11 @@
<head>
<title>{% block title %}{{ title }}{% endblock %}</title>
<script src="/assets/js/htmx.min.js"></script>
<script src="//unpkg.com/alpinejs" defer></script>
<script src="/assets/js/htmx@2.0.1.min.js"></script>
<script src="/assets/js/alpinejs@3.14.1.min.js" defer></script>
<link href="/assets/css/style.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/flowbite@2.5.1/dist/flowbite.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/flowbite@2.5.1/dist/flowbite.min.js"></script>
<link href="/assets/css/flowbite@2.5.1.min.css" rel="stylesheet" />
<script src="/assets/js/flowbite@2.5.1.min.js"></script>
{% block head %}{% endblock %}
</head>

View File

@ -0,0 +1,3 @@
SESAM_FSV_VERSION=1.40.14
SESAM_FSV_LIB_PATH="C:/Program Files/santesocial/fsv/${SESAM_FSV_VERSION}/lib"
SESAM_FSV_SSVLIB=ssvw64

View File

@ -0,0 +1,2 @@
SESAM_FSV_VERSION=1.40.14
SESAM_INI_PATH=${ALLUSERSPROFILE}\\santesocial\\fsv\\${SESAM_FSV_VERSION}\\conf\\sesam.ini

View File

@ -1,2 +1,2 @@
SESAM_FSV_VERSION=1.40.13
SESAM_INI_PATH=${ALLUSERSPROFILE}\\santesocial\\fsv\\${SESAM_FSV_VERSION}\\conf\\sesam.ini
SESAM_FSV_VERSION=1.40.13
SESAM_INI_PATH=${ALLUSERSPROFILE}\\santesocial\\fsv\\${SESAM_FSV_VERSION}\\conf\\sesam.ini

View File

@ -16,7 +16,10 @@ fn main() {
// Add local lib directory to the linker search path (for def files and static libs)
let static_lib_path = manifest_path.join("lib");
println!("cargo::rustc-link-search=native={}", static_lib_path.display());
println!(
"cargo::rustc-link-search=native={}",
static_lib_path.display()
);
// Add the SESAM_FSV_LIB_PATH to the linker search path
let fsv_lib_path = PathBuf::from(env::var("SESAM_FSV_LIB_PATH").unwrap());
@ -31,6 +34,9 @@ fn main() {
}
// Link the SESAM_FSV_SSVLIB dynamic library
println!("cargo::rustc-link-lib=dylib={}", env::var("SESAM_FSV_SSVLIB").unwrap());
println!(
"cargo::rustc-link-lib=dylib={}",
env::var("SESAM_FSV_SSVLIB").unwrap()
);
// TODO : try `raw-dylib` instead of `dylib` on Windows to avoid the need of the `lib` headers compiled from the `def`
}

Binary file not shown.

View File

@ -0,0 +1,876 @@
use libc::{c_void, size_t};
use std::ffi::CString;
use std::ptr;
use crate::libssv::SSV_LireDroitsVitale;
use crate::ssv_memory::{decode_ssv_memory, Block};
#[derive(Debug, Default)]
pub struct CarteVitale {
donneesAssure: DonneesAssure,
serviceAMOFamille: ServiceAMOFamille,
donneesAccidentTravail: DonneesAccidentTravail,
donneesBeneficiaire: Vec<DonneesBeneficiaire>,
}
// 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 DonneesAssure {
type_de_carte_vitale: char, // CA
numero_de_serie: String, // CN - 1 -> 20
date_de_fin_de_validite: String, // CN - Format AAAAMMJJ0000
donnees_administration_ruf1: char, // CN
donnees_administration_ruf2: String, // CA - 24
donnees_ruf_administration: String, // CE - 10
type_d_identification_du_porteur: char, // CA
numero_national_d_immatriculation: String, // CA - 13
cle_du_nir: String, // CA - 2
code_regime: String, // CN - 2
caisse_gestionnaire: String, // CN - 3
centre_gestionnaire: String, // CN - 4
code_gestion: String, // CA - 2
donnees_ruf_famille: String, // CE - 55
}
#[derive(Debug, Default)]
struct ServiceAMOFamille {
code_service_amo_famille: String, // CN - 2
date_de_debut_service_amo_famille: String, // CN - Format AAAAMMJJ0000
date_de_fin_service_amo_famille: String, // CN - Format AAAAMMJJ0000
}
#[derive(Debug, Default)]
struct DonneesAccidentTravail {
organisme_gestionnaire_risque_at: String, // CN - 9
code_at: char, // CA - 2
identifiant_at: String, // CN - 9
organisme_gestionnaire_at1: String, // CN - 9
code_at1: char, // CA - 2
identifiant_at1: String, // CN - 9
organisme_gestionnaire_at2: String, // CN - 9
code_at2: char, // CA - 2
identifiant_at2: String, // CN - 9
}
#[derive(Debug, Default)]
struct DonneesBeneficiaire {
nom_usuel: String, // CE - 27
nom_de_famille: String, // CE - 27
prenom: String, // CE - 27
adresse_ligne_1: String, // CE - 32
adresse_ligne_2: String, // CE - 32
adresse_ligne_3: String, // CE - 32
adresse_ligne_4: String, // CE - 32
adresse_ligne_5: String, // CE - 32
nir_certifie: String, // CA - 13
cle_du_nir_certifie: String, // CN - 2
date_de_certification_du_nir: String, // CN - Format AAAAMMJJ0000
date_de_naissance: String, // CN - Format AAAAMMJJ0000
rang_de_naissance: String, // CN - 1
qualite: String, // CN - 2
code_service_amo_beneficiaire: char, // CA - 2
date_de_debut_service_amo: String, // CN - Format AAAAMMJJ0000
date_de_fin_service_amo: String, // CN - Format AAAAMMJJ0000
donnees_ruf_amo: String, // CE - 30
periodeDroitsAMO: Vec<PeriodeDroitsAMO>,
periodeCodeCouverture: Vec<PeriodeCodeCouverture>,
donneesMutuelle: DonneesMutuelle,
donneesComplementaire: DonneesComplementaire,
donneesRUFBeneficiaireComplementaire: DonneesRUFBeneficiaireComplementaire,
}
#[derive(Debug, Default)]
struct PeriodeDroitsAMO {
date_de_debut_droits_amo: String, // CN - Format AAAAMMJJ0000
date_de_fin_droits_amo: String, // CN - Format AAAAMMJJ0000
}
#[derive(Debug, Default)]
struct PeriodeCodeCouverture {
date_de_debut_code_couverture: String, // CN - Format AAAAMMJJ0000
date_de_fin_code_couverture: String, // CN - Format AAAAMMJJ0000
code_ald: String, // CN - 1
code_situation: String, // CN - 4
}
#[derive(Debug, Default)]
struct DonneesMutuelle {
identification_mutuelle: String, // CN - 8
garanties_effectives: String, // CA - 8
indicateur_de_traitement_mutuelle: String, // CN - 2
type_de_services_associes: char, // CA - 1
services_associes_au_contrat: String, // CA - 17
code_aiguillage_sts: char, // CA - 1
periodeDroitsMutuelle: Vec<PeriodeDroitsMutuelle>,
}
#[derive(Debug, Default)]
struct PeriodeDroitsMutuelle {
date_de_debut_droits_mutuelle: String, // CN - Format AAAAMMJJ0000
date_de_fin_droits_mutuelle: String, // CN - Format AAAAMMJJ0000
}
#[derive(Debug, Default)]
struct DonneesComplementaire {
numero_complementaire_b2: String, // CA - 10
numero_complementaire_edi: String, // CA - 19
numero_adherent_amc: String, // CA - 8
indicateur_de_traitement_amc: String, // CN - 2
date_de_debut_validite_amc: String, // CN - Format AAAAMMJJ0000
date_de_fin_validite_amc: String, // CN - Format AAAAMMJJ0000
code_routage_flux_amc: String, // CA - 2
identifiant_hote: String, // CA - 3
nom_de_domaine_amc: String, // CE - 20
code_aiguillage_sts: char, // CA - 1
type_de_services_associes: char, // CA - 1
services_associes_au_contrat: String, // CE - 17
}
#[derive(Debug, Default)]
struct DonneesRUFBeneficiaireComplementaire {
donnees_ruf_beneficiaire_complementaire: String, // CE - 115
}
pub fn LireDroitsVitale(
lecteurPS: &str,
lecteurVitale: &str,
codePorteurPS: &str,
dateConsultation: &str,
) -> Result<CarteVitale, String> {
let resource_ps = CString::new(lecteurPS).expect("CString::new failed");
let resource_vitale = CString::new(lecteurVitale).expect("CString::new failed");
let card_number = CString::new(codePorteurPS).expect("CString::new failed");
let date_consultation = CString::new(dateConsultation).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_LireDroitsVitale(
resource_ps.as_ptr(),
resource_vitale.as_ptr(),
card_number.as_ptr(),
date_consultation.as_ptr(),
&mut buffer,
&mut size,
);
println!("SSV_LireDroitsVitale result: {}", result);
if !buffer.is_null() {
hex_values = std::slice::from_raw_parts(buffer as *const u8, size);
}
}
let groups = decode_ssv_memory(hex_values, hex_values.len());
let OUT = decode_carte_vitale(groups);
unsafe {
if !buffer.is_null() {
libc::free(buffer); // ??? si liberer avant decode_ssv_memory et decode_carte_vitale alors hex_values n'est plus correct ???
}
}
OUT
}
fn decode_carte_vitale(groups: Vec<Block>) -> Result<CarteVitale, String> {
let mut cartevitale = CarteVitale::default();
for group in groups {
for field in group.content {
match (group.id, field.id) {
(104, 1) => {
cartevitale
.donneesBeneficiaire
.push(DonneesBeneficiaire::default());
}
(105, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeDroitsAMO
.push(PeriodeDroitsAMO::default());
}
(106, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeCodeCouverture
.push(PeriodeCodeCouverture::default());
}
(108, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.periodeDroitsMutuelle
.push(PeriodeDroitsMutuelle::default());
}
_ => {}
}
if field.content.len() > 0 {
match (group.id, field.id) {
(101, 1) => {
let byte = field.content[0];
cartevitale.donneesAssure.type_de_carte_vitale = byte as char;
}
(101, 2) => {
cartevitale.donneesAssure.numero_de_serie = //bytes_to_decimal_string(&field.content);
String::from_utf8_lossy(field.content).to_string();
}
(101, 3) => {
cartevitale.donneesAssure.date_de_fin_de_validite =
String::from_utf8_lossy(field.content).to_string();
}
(101, 4) => {
println!("101.4 field.size: {:#?}", field.size);
let byte = field.content[0];
cartevitale.donneesAssure.donnees_administration_ruf1 = byte as char;
}
(101, 5) => {
cartevitale.donneesAssure.donnees_administration_ruf2 =
String::from_utf8_lossy(field.content).to_string();
}
(101, 6) => {
cartevitale.donneesAssure.donnees_ruf_administration =
String::from_utf8_lossy(field.content).to_string();
}
(101, 7) => {
let byte = field.content[0];
cartevitale.donneesAssure.type_d_identification_du_porteur = byte as char;
}
(101, 8) => {
cartevitale.donneesAssure.numero_national_d_immatriculation =
String::from_utf8_lossy(field.content).to_string();
}
(101, 9) => {
cartevitale.donneesAssure.cle_du_nir =
String::from_utf8_lossy(field.content).to_string();
}
(101, 10) => {
cartevitale.donneesAssure.code_regime =
String::from_utf8_lossy(field.content).to_string();
}
(101, 11) => {
cartevitale.donneesAssure.caisse_gestionnaire =
String::from_utf8_lossy(field.content).to_string();
}
(101, 12) => {
cartevitale.donneesAssure.centre_gestionnaire =
String::from_utf8_lossy(field.content).to_string();
}
(101, 13) => {
cartevitale.donneesAssure.code_gestion =
String::from_utf8_lossy(field.content).to_string();
}
(101, 14) => {
cartevitale.donneesAssure.donnees_ruf_famille =
String::from_utf8_lossy(field.content).to_string();
}
(102, 1) => {
cartevitale.serviceAMOFamille.code_service_amo_famille =
String::from_utf8_lossy(field.content).to_string();
}
(102, 2) => {
cartevitale
.serviceAMOFamille
.date_de_debut_service_amo_famille =
String::from_utf8_lossy(field.content).to_string();
}
(102, 3) => {
cartevitale
.serviceAMOFamille
.date_de_fin_service_amo_famille =
String::from_utf8_lossy(field.content).to_string();
}
(103, 1) => {
cartevitale
.donneesAccidentTravail
.organisme_gestionnaire_risque_at =
String::from_utf8_lossy(field.content).to_string();
}
(103, 2) => {
let byte = field.content[0];
cartevitale.donneesAccidentTravail.code_at = byte as char;
}
(103, 3) => {
cartevitale.donneesAccidentTravail.identifiant_at =
String::from_utf8_lossy(field.content).to_string();
}
(103, 4) => {
cartevitale
.donneesAccidentTravail
.organisme_gestionnaire_at1 =
String::from_utf8_lossy(field.content).to_string();
}
(103, 5) => {
let byte = field.content[0];
cartevitale.donneesAccidentTravail.code_at1 = byte as char;
}
(103, 6) => {
cartevitale.donneesAccidentTravail.identifiant_at1 =
String::from_utf8_lossy(field.content).to_string();
}
(103, 7) => {
cartevitale
.donneesAccidentTravail
.organisme_gestionnaire_at2 =
String::from_utf8_lossy(field.content).to_string();
}
(103, 8) => {
let byte = field.content[0];
cartevitale.donneesAccidentTravail.code_at2 = byte as char;
}
(103, 9) => {
cartevitale.donneesAccidentTravail.identifiant_at2 =
String::from_utf8_lossy(field.content).to_string();
}
(104, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.nom_usuel = String::from_utf8_lossy(field.content).to_string();
}
(104, 2) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.nom_de_famille = String::from_utf8_lossy(field.content).to_string();
}
(104, 3) => {
cartevitale.donneesBeneficiaire.last_mut().unwrap().prenom =
String::from_utf8_lossy(field.content).to_string();
}
(104, 4) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.adresse_ligne_1 = String::from_utf8_lossy(field.content).to_string();
}
(104, 5) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.adresse_ligne_2 = String::from_utf8_lossy(field.content).to_string();
}
(104, 6) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.adresse_ligne_3 = String::from_utf8_lossy(field.content).to_string();
}
(104, 7) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.adresse_ligne_4 = String::from_utf8_lossy(field.content).to_string();
}
(104, 8) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.adresse_ligne_5 = String::from_utf8_lossy(field.content).to_string();
}
(104, 9) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.nir_certifie = String::from_utf8_lossy(field.content).to_string();
}
(104, 10) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.cle_du_nir_certifie =
String::from_utf8_lossy(field.content).to_string();
}
(104, 11) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.date_de_certification_du_nir =
String::from_utf8_lossy(field.content).to_string();
}
(104, 12) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.date_de_naissance = String::from_utf8_lossy(field.content).to_string();
}
(104, 13) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.rang_de_naissance = String::from_utf8_lossy(field.content).to_string();
}
(104, 14) => {
cartevitale.donneesBeneficiaire.last_mut().unwrap().qualite =
String::from_utf8_lossy(field.content).to_string();
}
(104, 15) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.code_service_amo_beneficiaire = field.content[0] as char;
}
(104, 16) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.date_de_debut_service_amo =
String::from_utf8_lossy(field.content).to_string();
}
(104, 17) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.date_de_fin_service_amo =
String::from_utf8_lossy(field.content).to_string();
}
(104, 18) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donnees_ruf_amo = String::from_utf8_lossy(field.content).to_string();
}
(105, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeDroitsAMO
.last_mut()
.unwrap()
.date_de_debut_droits_amo =
String::from_utf8_lossy(field.content).to_string();
}
(105, 2) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeDroitsAMO
.last_mut()
.unwrap()
.date_de_fin_droits_amo =
String::from_utf8_lossy(field.content).to_string();
}
(106, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeCodeCouverture
.last_mut()
.unwrap()
.date_de_debut_code_couverture =
String::from_utf8_lossy(field.content).to_string();
}
(106, 2) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeCodeCouverture
.last_mut()
.unwrap()
.date_de_fin_code_couverture =
String::from_utf8_lossy(field.content).to_string();
}
(106, 3) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeCodeCouverture
.last_mut()
.unwrap()
.code_ald = String::from_utf8_lossy(field.content).to_string();
}
(106, 4) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.periodeCodeCouverture
.last_mut()
.unwrap()
.code_situation = String::from_utf8_lossy(field.content).to_string();
}
(107, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.identification_mutuelle =
String::from_utf8_lossy(field.content).to_string();
}
(107, 2) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.garanties_effectives =
String::from_utf8_lossy(field.content).to_string();
}
(107, 3) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.indicateur_de_traitement_mutuelle =
String::from_utf8_lossy(field.content).to_string();
}
(107, 4) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.type_de_services_associes = field.content[0] as char;
}
(107, 5) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.services_associes_au_contrat =
String::from_utf8_lossy(field.content).to_string();
}
(107, 6) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.code_aiguillage_sts = field.content[0] as char;
}
(108, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.periodeDroitsMutuelle
.last_mut()
.unwrap()
.date_de_debut_droits_mutuelle =
String::from_utf8_lossy(field.content).to_string();
}
(108, 2) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesMutuelle
.periodeDroitsMutuelle
.last_mut()
.unwrap()
.date_de_fin_droits_mutuelle =
String::from_utf8_lossy(field.content).to_string();
}
(109, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.numero_complementaire_b2 =
String::from_utf8_lossy(field.content).to_string();
}
(109, 2) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.numero_complementaire_edi =
String::from_utf8_lossy(field.content).to_string();
}
(109, 3) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.numero_adherent_amc =
String::from_utf8_lossy(field.content).to_string();
}
(109, 4) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.indicateur_de_traitement_amc =
String::from_utf8_lossy(field.content).to_string();
}
(109, 5) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.date_de_debut_validite_amc =
String::from_utf8_lossy(field.content).to_string();
}
(109, 6) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.date_de_fin_validite_amc =
String::from_utf8_lossy(field.content).to_string();
}
(109, 7) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.code_routage_flux_amc =
String::from_utf8_lossy(field.content).to_string();
}
(109, 8) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.identifiant_hote = String::from_utf8_lossy(field.content).to_string();
}
(109, 9) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.nom_de_domaine_amc =
String::from_utf8_lossy(field.content).to_string();
}
(109, 10) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.code_aiguillage_sts = field.content[0] as char;
}
(109, 11) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.type_de_services_associes = field.content[0] as char;
}
(109, 12) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesComplementaire
.services_associes_au_contrat =
String::from_utf8_lossy(field.content).to_string();
}
(111, 1) => {
cartevitale
.donneesBeneficiaire
.last_mut()
.unwrap()
.donneesRUFBeneficiaireComplementaire
.donnees_ruf_beneficiaire_complementaire =
String::from_utf8_lossy(field.content).to_string();
}
_ => {
return Err(format!(
"Unknown (group, field) pair: ({}, {})",
group.id, field.id
))
}
}
}
}
}
Ok(cartevitale)
}
// #[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!();
// }
// }

View File

@ -1,4 +1,5 @@
pub mod cps;
pub mod cartevitale;
pub mod libssv;
pub mod ssv_memory;
pub mod ssvlib_demo;

View File

@ -19,5 +19,13 @@ extern "C" {
ZDonneesSortie: *mut *mut c_void,
TTailleDonneesSortie: *mut size_t,
) -> c_ushort;
pub fn SSV_LireDroitsVitale(
NomRessourcePS: *const c_char,
NomRessourceLecteur: *const c_char,
CodePorteurPS: *const c_char,
DateConsultation: *const c_char,
ZDonneesSortie: *mut *mut c_void,
TTailleDonneesSortie: *mut size_t,
) -> c_ushort;
}
// TODO : replace void* by Rust struct : https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs

View File

@ -1,4 +1,5 @@
mod cps;
mod cartevitale;
mod libssv;
mod ssv_memory;
mod ssvlib_demo;

View File

@ -71,6 +71,8 @@ impl<'a> From<&'a [u8]> for Block<'a> {
let mut field_id = 1;
while field_offset < block_size {
let mut field: Field<'a> = raw_content[field_offset..].into();
field.id = field_id;
field_offset += field.size;
field_id += 1;

View File

@ -7,7 +7,10 @@ use std::ffi::CString;
use std::path::PathBuf;
use std::ptr;
use crate::cps::lire_carte;
use crate::cartevitale::LireDroitsVitale;
use crate::libssv::{SSV_InitLIB2, SSV_LireConfig};
fn ssv_init_lib_2() {
@ -51,10 +54,15 @@ pub fn demo() {
let code_pin = "1234";
let lecteur = "HID Global OMNIKEY 3x21 Smart Card Reader 0";
let lecteurvitale = "HID Global OMNIKEY 3x21 Smart Card Reader 1";
let dateconsultation ="20240813";
let carte_ps = lire_carte(code_pin, lecteur).unwrap();
println!("CartePS: {:#?}", carte_ps);
ssv_lire_config();
let carte_vitale = LireDroitsVitale(lecteur, lecteurvitale, code_pin, dateconsultation).unwrap();
println!("carte_vitale: {:#?}", carte_vitale);
println!("-----------------------------------------");
}

View File

@ -3,3 +3,4 @@ EXPORTS
SSV_InitLIB2
SSV_LireCartePS
SSV_LireConfig
SSV_LireDroitsVitale

View File

@ -0,0 +1,26 @@
@echo off
rem Set variables
set LIB_DIR=crates/sesam-vitale/lib
set DEF_DIR=crates/sesam-vitale/src/win/fsv
if "%1"=="/clean" (
goto clean
)
rem Set the environment for the x64 platform
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
rem Create a ssvw64.lib file from the ssvw64.def file
lib /def:ssvw64.def /out:%LIB_DIR%\ssvw64.lib /machine:x64
rem Build complete
pause
exit /b 0
:clean
del %LIB_DIR%\*.lib
del %LIB_DIR%\*.exp
rem Clean complete
pause
exit /b 0

View File

@ -1,7 +1,7 @@
@echo off
rem Set variables
set LIB_DIR=crates/sesam-vitale/lib
set DEF_DIR=crates/sesam-vitale/src/win/fsv
set LIB_DIR=F:\PAPILLON\Krys4lideCopie\crates\sesam-vitale\lib
set DEF_DIR=F:\PAPILLON\Krys4lideCopie\crates\sesam-vitale\src\win\fsv
if "%1"=="/clean" (
goto clean

6
scripts/ssvw64.def Normal file
View File

@ -0,0 +1,6 @@
LIBRARY "ssvw64"
EXPORTS
SSV_InitLIB2
SSV_LireCartePS
SSV_LireConfig
SSV_LireDroitsVitale