Rust : décompresser en flux un fichier JSONlines compressé en XZ et afficher un attribut unique

Ce programme d’exemple analyse un fichier JSONlines compressé en XZ et affiche l’attribut timestamp de chaque ligne s’il est présent. Les lignes invalides ou vides sont ignorées.

main.rs

stream_decompress.rs
//! Ce programme lit en flux et décompresse un fichier JSONlines compressé en XZ,
//! analyse chaque ligne comme du JSON, et affiche le champ "timestamp" s'il est présent.
//! Les lignes invalides ou vides sont ignorées.

use std::fs::File; // Pour ouvrir le fichier
use std::io::{BufRead, BufReader}; // Pour la lecture bufferisée
use anyhow::{Context, Result}; // Pour la gestion des erreurs
use serde::Deserialize; // Pour désérialiser le JSON
use xz2::read::XzDecoder; // Pour la décompression XZ

/// Représente une ligne unique dans le fichier JSONlines.
/// Seul le champ "timestamp" est extrait.
#[derive(Deserialize, Debug)]
struct Line {
    timestamp: Option<f64>,
}

/// Traite le fichier JSONlines compressé en XZ donné.
/// Le décompresse à la volée, analyse chaque ligne comme du JSON,
/// et affiche l'horodatage s'il est disponible. Ignore les lignes invalides.
fn process_file(path: &str) -> Result<()> {
    // Ouvrir le fichier
    let file = File::open(path).with_context(|| format!("Échec de l'ouverture du fichier : {}", path))?;
    // Créer un lecteur bufferisé pour l'efficacité
    let reader = BufReader::new(file);
    // Envelopper avec le décompresseur XZ
    let decompressor = XzDecoder::new(reader);
    // Un autre lecteur bufferisé pour la lecture ligne par ligne
    let buf_reader = BufReader::new(decompressor);

    // Itérer sur chaque ligne
    for line_result in buf_reader.lines() {
        let line = match line_result {
            Ok(l) => l,
            Err(_) => continue, // Ignorer les lignes avec des erreurs d'E/S
        };
        // Ignorer les lignes vides
        if line.trim().is_empty() {
            continue;
        }
        // Essayer d'analyser comme du JSON
        match serde_json::from_str::<Line>(&line) {
            Ok(obj) => {
                // Afficher l'horodatage s'il est présent
                if let Some(ts) = obj.timestamp {
                    println!("{}", ts);
                }
            }
            Err(e) => {
                eprintln!("Échec de l'analyse de la ligne JSON : {}", e);
                continue;
            }
        }
    }
    Ok(())
}

/// Point d'entrée principal.
/// Prend le chemin du fichier comme premier argument, ou utilise le fichier d'exemple par défaut.
fn main() -> Result<()> {
    // Exiger le chemin du fichier comme premier argument positionnel.
    // S'il manque, afficher un court message d'utilisation et sortir avec un code non nul.
    let mut args = std::env::args();
    let prog = args.next().unwrap_or_else(|| "rust_parse_status_logs".to_string());
    let path = match args.next() {
        Some(p) => p,
        None => {
            eprintln!("Utilisation : {} <chemin-vers-fichier-jsonlines-xz>", prog);
            std::process::exit(2);
        }
    };

    // Traiter le fichier
    process_file(&path)
}

Cargo.toml

Cargo.toml
[package]
name = "rust_parse_status_logs"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
xz2 = "0.1"
anyhow = "1"

Exemple d’entrée

example.json
{"timestamp": 1744267290.991, "status": "ok"}
{"timestamp": 1744267291.987, "status": "ok"}
{"status": "missing timestamp"}
invalid json line

Notez que vous devez compresser ce fichier avec XZ pour créer le fichier d’entrée du programme.

compress_example.sh
xz example.json

Compilation et exécution

run_decompress_example.sh
cargo run -- example.json.xz

Sortie attendue :

decompress_output.txt
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/rust_parse_status_logs example.json.xz`
1744267290.991
1744267291.987
Failed to parse JSON line: expected value at line 1 column 1

Check out similar posts by category: Rust