majorsilence-reporting
Générez des rapports RDL en PDF, Excel, CSV et plus depuis Rust — via une FFI native en processus grâce à libloading. Aucun runtime .NET. Aucun sous-processus. Handle de bibliothèque thread-safe, adossé à Arc. Une seule dépendance.
Installation
Le crate n'est pas encore publié sur crates.io. Ajoutez-le comme dépendance locale (path) :
# Cargo.toml [dependencies] majorsilence-reporting = { path = "/path/to/reporting-rust" } # Une fois publié sur crates.io : # majorsilence-reporting = "1.0"
La seule dépendance en runtime est libloading = "0.8". Édition Rust 2021, Rust 1.65 minimum.
Démarrage rapide
use majorsilence_reporting::RdlLibrary; fn main() -> Result<(), Box<dyn std::error::Error>> { // Charger la bibliothèque native une fois par processus. let lib = RdlLibrary::load("/path/to/rdlnative/librdlnative.so")?; // Construire une configuration de rapport. let mut rpt = lib.report("/path/to/report.rdl"); rpt.set_connection_string("Data Source=/path/to/northwindEF.db") .set_parameter("Country", "Germany"); // Exporter vers un fichier. rpt.export("pdf", "/tmp/output.pdf")?; // Ou exporter en mémoire — aucun fichier temporaire écrit. let pdf_bytes: Vec<u8> = rpt.export_to_memory("pdf")?; println!("{} octets obtenus", pdf_bytes.len()); Ok(()) }
Chargement de la bibliothèque
RdlLibrary::load effectue toute la configuration nécessaire : il définit RDLNATIVE_LIB_DIR, précharge les fichiers .so / .dylib associés avec RTLD_GLOBAL afin que le résolveur P/Invoke .NET puisse les trouver, résout tous les symboles FFI, et appelle rdl_init().
use majorsilence_reporting::RdlLibrary; // À appeler une fois au démarrage du processus. RdlLibrary est adossé à Arc — clonage peu coûteux et Send + Sync. let lib = RdlLibrary::load("/path/to/librdlnative.so")?; // Le cloner entre threads (par ex. dans un état Axum ou une Data Actix) let lib2 = lib.clone(); std::thread::spawn(move || { let mut rpt = lib2.report("/path/to/report.rdl"); rpt.export("pdf", "/tmp/thread.pdf").unwrap(); });
LD_LIBRARY_PATH (Linux) ou DYLD_LIBRARY_PATH (macOS) doit inclure le répertoire de la bibliothèque native avant le démarrage du processus. RdlLibrary::load gère RDLNATIVE_LIB_DIR et le préchargement des bibliothèques associées, mais ne peut pas modifier rétroactivement le chemin de recherche de l'éditeur de liens dynamique pour le processus en cours.
API principale
Les méthodes de construction renvoient &mut Self pour permettre le chaînage. Un nouveau handle de rapport natif est ouvert puis fermé à chaque appel à export ou export_to_memory, si bien qu'une valeur Report peut être réutilisée pour plusieurs rendus.
| Méthode | Description |
|---|---|
| RdlLibrary::load(path) | Charge la bibliothèque native et initialise la table de symboles FFI. Renvoie Result<RdlLibrary, Error>. À appeler une fois par processus. |
| lib.report(rdl_path) | Crée une valeur Report liée à un fichier .rdl. Peu coûteux — aucun handle natif n'est encore ouvert. |
| rpt.set_connection_string(cs) | Définit la chaîne de connexion à la base de données. Renvoie &mut Self. |
| rpt.set_parameter(name, value) | Définit un paramètre de rapport nommé. Renvoie &mut Self. Peut être appelée autant de fois que nécessaire. |
| rpt.add_data(name, rows) | Fournit un jeu de données en mémoire. rows est de type Vec<HashMap<String, String>>. Renvoie &mut Self. Voir Jeux de données en mémoire. |
| rpt.export(format, path) | Génère le rendu et l'écrit vers un chemin de fichier. Renvoie Result<(), Error>. |
| rpt.export_to_memory(format) | Génère le rendu et renvoie les octets. Renvoie Result<Vec<u8>, Error>. Aucun fichier temporaire n'est écrit. |
Jeux de données en mémoire
Transmettez des données structurées directement au rapport sans base de données. Chaque ligne est un HashMap<String, String> dont les clés sont les noms de colonnes et les valeurs les champs encodés en chaînes.
use std::collections::HashMap; use majorsilence_reporting::RdlLibrary; let lib = RdlLibrary::load("/path/to/librdlnative.so")?; let mut rpt = lib.report("/path/to/report.rdl"); let rows: Vec<HashMap<String, String>> = vec![ [("Product", "PDF Library Pro"), ("Revenue", "1200.00"), ("Units", "3")] .iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(), [("Product", "Report Designer"), ("Revenue", "250.00"), ("Units", "1")] .iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(), ]; rpt.add_data("SalesData", rows); let pdf = rpt.export_to_memory("pdf")?;
.rdl. Toutes les valeurs de champs doivent être des chaînes — convertissez les nombres, dates et booléens avant de les insérer dans la map. Plusieurs jeux de données peuvent être fournis avec plusieurs appels à add_data.
Export en mémoire
export_to_memory appelle la fonction FFI native rdl_report_render_buffer, qui remplit un tampon alloué côté natif et renvoie un pointeur et une longueur. Le wrapper Rust copie les octets dans un Vec<u8> et appelle rdl_free. Aucun fichier temporaire n'est écrit à aucun moment — utile pour les corps de réponse HTTP ou l'intégration dans un pipeline.
// Exemple de handler Axum async fn report_handler(State(lib): State<RdlLibrary>) -> impl IntoResponse { let mut rpt = lib.report("/reports/sales.rdl"); rpt.set_connection_string("Data Source=/data/northwindEF.db"); match rpt.export_to_memory("pdf") { Ok(bytes) => ( StatusCode::OK, [("content-type", "application/pdf")], bytes, ).into_response(), Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), } }
Gestion des erreurs
Toutes les opérations faillibles renvoient Result<T, majorsilence_reporting::Error>. Error implémente std::error::Error et encapsule le message renvoyé par la fonction native rdl_last_error().
match rpt.export_to_memory("pdf") { Ok(bytes) => { /* utiliser bytes */ } Err(e) => eprintln!("échec du rendu : {e}"), } // Ou propager avec ? let bytes = rpt.export_to_memory("pdf")?;
"pdf". Pour énumérer les formats valides à la compilation, utilisez la constante publique majorsilence_reporting::VALID_FORMATS: &[&str].
Formats d'export
Les dix formats sont pris en charge. Contrairement aux modes sous-processus de Python/PHP/Ruby, tous les formats sont disponibles dans le wrapper Rust car il utilise toujours le chemin FFI natif.
| Chaîne de format | Sortie |
|---|---|
pdf | PDF (aussi le format de repli pour toute chaîne non reconnue) |
csv | Valeurs séparées par des virgules |
xlsx | Classeur Excel |
xlsx_table | Classeur Excel (style tableau) |
xml | Données XML |
rtf | Rich Text Format |
tif | Image TIFF (couleur) |
tifb | Image TIFF (noir & blanc) |
html | HTML |
mht | Archive web MHTML |