← SDKs
SDK de Rust

majorsilence-reporting

Genera informes RDL en PDF, Excel, CSV y más desde Rust — mediante FFI nativa en proceso a través de libloading. Sin runtime de .NET. Sin subprocesos. Handle de biblioteca seguro para hilos, respaldado por Arc. Una sola dependencia.


Instalación

El crate aún no está publicado en crates.io. Añádelo como dependencia de ruta (path):

# Cargo.toml
[dependencies]
majorsilence-reporting = { path = "/path/to/reporting-rust" }

# Una vez publicado en crates.io:
# majorsilence-reporting = "1.0"

La única dependencia en tiempo de ejecución es libloading = "0.8". Edición 2021 de Rust, versión mínima 1.65.


Inicio rápido

use majorsilence_reporting::RdlLibrary;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Cargar la biblioteca nativa una vez por proceso.
    let lib = RdlLibrary::load("/path/to/rdlnative/librdlnative.so")?;

    // Construir una configuración de informe.
    let mut rpt = lib.report("/path/to/report.rdl");
    rpt.set_connection_string("Data Source=/path/to/northwindEF.db")
       .set_parameter("Country", "Germany");

    // Exportar a un archivo.
    rpt.export("pdf", "/tmp/output.pdf")?;

    // O exportar a memoria — no se escribe ningún archivo temporal.
    let pdf_bytes: Vec<u8> = rpt.export_to_memory("pdf")?;
    println!("Se obtuvieron {} bytes", pdf_bytes.len());

    Ok(())
}

Carga de la biblioteca

RdlLibrary::load realiza toda la configuración necesaria: define RDLNATIVE_LIB_DIR, precarga los archivos .so / .dylib asociados con RTLD_GLOBAL para que el resolutor P/Invoke de .NET pueda encontrarlos, resuelve todos los símbolos FFI, y llama a rdl_init().

use majorsilence_reporting::RdlLibrary;

// Llamar una vez al iniciar el proceso. RdlLibrary está respaldado por Arc — clonación económica y Send + Sync.
let lib = RdlLibrary::load("/path/to/librdlnative.so")?;

// Clonarlo entre hilos (por ejemplo en un estado de Axum o Data de 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) o DYLD_LIBRARY_PATH (macOS) debe incluir el directorio de la biblioteca nativa antes de que arranque el proceso. RdlLibrary::load gestiona RDLNATIVE_LIB_DIR y la precarga de bibliotecas asociadas, pero no puede modificar retroactivamente la ruta de búsqueda del enlazador dinámico para el proceso en ejecución.

API principal

Los métodos constructores devuelven &mut Self para permitir encadenamiento. Se abre y se cierra un nuevo handle de informe nativo en cada llamada a export o export_to_memory, por lo que un valor Report puede reutilizarse en varios renderizados.

MétodoDescripción
RdlLibrary::load(path)Carga la biblioteca nativa e inicializa la tabla de símbolos FFI. Devuelve Result<RdlLibrary, Error>. Llamar una vez por proceso.
lib.report(rdl_path)Crea un valor Report vinculado a un archivo .rdl. Económico — todavía no se abre ningún handle nativo.
rpt.set_connection_string(cs)Establece la cadena de conexión a la base de datos. Devuelve &mut Self.
rpt.set_parameter(name, value)Define un parámetro de informe con nombre. Devuelve &mut Self. Se puede llamar tantas veces como sea necesario.
rpt.add_data(name, rows)Suministra un conjunto de datos en memoria. rows es de tipo Vec<HashMap<String, String>>. Devuelve &mut Self. Ver Conjuntos de datos en memoria.
rpt.export(format, path)Renderiza y escribe en una ruta de archivo. Devuelve Result<(), Error>.
rpt.export_to_memory(format)Renderiza y devuelve los bytes. Devuelve Result<Vec<u8>, Error>. No se escribe ningún archivo temporal.

Conjuntos de datos en memoria

Pasa datos estructurados directamente al informe sin necesidad de una base de datos. Cada fila es un HashMap<String, String> cuyas claves son los nombres de columna y cuyos valores son los campos codificados como cadenas.

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")?;
El nombre del conjunto de datos debe coincidir con un conjunto de datos definido en el archivo .rdl. Todos los valores de campo deben ser cadenas — convierte números, fechas y booleanos antes de insertarlos en el mapa. Se pueden suministrar varios conjuntos de datos con varias llamadas a add_data.

Exportar a memoria

export_to_memory llama a la función FFI nativa rdl_report_render_buffer, que rellena un búfer asignado del lado nativo y devuelve un puntero y una longitud. El wrapper de Rust copia los bytes en un Vec<u8> y llama a rdl_free. No se escribe ningún archivo temporal en ningún momento — útil para cuerpos de respuesta HTTP o integración en pipelines.

// Ejemplo de handler de 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(),
    }
}

Gestión de errores

Todas las operaciones que pueden fallar devuelven Result<T, majorsilence_reporting::Error>. Error implementa std::error::Error y encapsula el mensaje devuelto por la función nativa rdl_last_error().

match rpt.export_to_memory("pdf") {
    Ok(bytes) => { /* usar bytes */ }
    Err(e)    => eprintln!("fallo al renderizar: {e}"),
}

// O propagar con ?
let bytes = rpt.export_to_memory("pdf")?;
Una cadena de formato no reconocida no devuelve un error — pasa silenciosamente a "pdf". Para enumerar los formatos válidos en tiempo de compilación, usa la constante pública majorsilence_reporting::VALID_FORMATS: &[&str].

Formatos de exportación

Se admiten los diez formatos. A diferencia de los modos de subproceso de Python/PHP/Ruby, todos los formatos están disponibles en el wrapper de Rust porque siempre usa la vía FFI nativa.

Cadena de formatoSalida
pdfPDF (también el valor de reserva para cualquier cadena no reconocida)
csvValores separados por comas
xlsxLibro de Excel
xlsx_tableLibro de Excel (estilo tabla)
xmlDatos XML
rtfRich Text Format
tifImagen TIFF (color)
tifbImagen TIFF (blanco y negro)
htmlHTML
mhtArchivo web MHTML