← SDK
SDK Rust

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éthodeDescription
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")?;
Le nom du jeu de données doit correspondre à un jeu de données défini dans le fichier .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")?;
Une chaîne de format non reconnue ne renvoie pas d'erreur — elle bascule silencieusement vers "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 formatSortie
pdfPDF (aussi le format de repli pour toute chaîne non reconnue)
csvValeurs séparées par des virgules
xlsxClasseur Excel
xlsx_tableClasseur Excel (style tableau)
xmlDonnées XML
rtfRich Text Format
tifImage TIFF (couleur)
tifbImage TIFF (noir & blanc)
htmlHTML
mhtArchive web MHTML