Skip to content

Instantly share code, notes, and snippets.

@horaciod
Last active January 15, 2026 13:50
Show Gist options
  • Select an option

  • Save horaciod/858fdeb5632abe806ef2707c03bbf5cd to your computer and use it in GitHub Desktop.

Select an option

Save horaciod/858fdeb5632abe806ef2707c03bbf5cd to your computer and use it in GitHub Desktop.
hace backup de un motor solr
#!/usr/bin/env rust-script
### Help: rust-script nombre_del_script.rs --help
//! ```cargo
//! [dependencies]
//! reqwest = { version = "0.11", features = ["blocking", "json"] }
//! serde_json = "1.0"
//! indicatif = "0.17"
//! clap = { version = "4.0", features = ["derive"] }
//! ```
use std::fs::File;
use std::io::{BufWriter, Write};
use serde_json::{Value};
use reqwest::blocking::Client;
use indicatif::{ProgressBar, ProgressStyle};
use clap::Parser; // Importamos el derivador de argumentos
// Definimos la estructura de los argumentos que aceptará el script
#[derive(Parser, Debug)]
#[command(author, version, about = "Solr Backup Script", long_about = None)]
struct Args {
/// URL de Solr (ej. http://localhost:8985/solr/biblio/select)
#[arg(short, long, default_value = "http://localhost:8985/solr/biblio/select")]
solr_url: String,
/// Nombre del archivo de salida
#[arg(short, long, default_value = "backup_solr.json")]
output_file: String,
/// Cantidad de documentos por lote
#[arg(short, long, default_value_t = 1000)]
batch_size: usize,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 0. Parsear los argumentos de la línea de comandos
let args = Args::parse();
let client = Client::new();
// 1. Obtener el total de documentos (Usamos args.solr_url)
let initial_resp: Value = client.get(&args.solr_url)
.query(&[("q", "*:*"), ("rows", "0"), ("wt", "json")])
.send()?.json()?;
let total_docs = initial_resp["response"]["numFound"].as_u64().unwrap_or(0);
// 2. Configurar la barra de progreso
let pb = ProgressBar::new(total_docs);
pb.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")?
.progress_chars("#>-"));
// 3. Preparar archivo (Usamos args.output_file)
let file = File::create(&args.output_file)?;
let mut writer = BufWriter::new(file);
writer.write_all(b"[\n")?;
let mut cursor_mark = "*".to_string();
let mut is_first_batch = true;
// 4. Bucle de exportación
loop {
let response: Value = client.get(&args.solr_url)
.query(&[
("q", "*:*"),
("rows", &args.batch_size.to_string()), // Usamos args.batch_size
("sort", "id asc"),
("cursorMark", &cursor_mark),
("wt", "json")
])
.send()?
.json()?;
let docs = response["response"]["docs"].as_array().ok_or("Error al leer docs")?;
let next_cursor_mark = response["nextCursorMark"].as_str().ok_or("No se encontró nextCursorMark")?;
if docs.is_empty() { break; }
for (i, doc) in docs.iter().enumerate() {
if !is_first_batch || i > 0 {
writer.write_all(b",\n")?;
}
serde_json::to_writer(&mut writer, &doc)?;
}
pb.inc(docs.len() as u64);
if cursor_mark == next_cursor_mark { break; }
cursor_mark = next_cursor_mark.to_string();
is_first_batch = false;
}
writer.write_all(b"\n]")?;
writer.flush()?;
pb.finish_with_message("Backup completado");
println!("\nArchivo guardado: {}", args.output_file);
Ok(())
}
@horaciod
Copy link
Author

Para lograr esto en Rust, la forma más eficiente y limpia es utilizar la biblioteca clap (Command Line Argument Parser). Esta librería permite definir argumentos, valores por defecto y generar automáticamente un mensaje de ayuda (--help).

Aquí tienes el código actualizado. He modificado la sección de dependencias y la lógica inicial para capturar estos parámetros:

#!/usr/bin/env rust-script

//! ```cargo
//! [dependencies]
//! reqwest = { version = "0.11", features = ["blocking", "json"] }
//! serde_json = "1.0"
//! indicatif = "0.17"
//! clap = { version = "4.0", features = ["derive"] }
//! ```

use std::fs::File;
use std::io::{BufWriter, Write};
use serde_json::{Value};
use reqwest::blocking::Client;
use indicatif::{ProgressBar, ProgressStyle};
use clap::Parser; // Importamos el derivador de argumentos

// Definimos la estructura de los argumentos que aceptará el script
#[derive(Parser, Debug)]
#[command(author, version, about = "Solr Backup Script", long_about = None)]
struct Args {
    /// URL de Solr (ej. http://localhost:8985/solr/biblio/select)
    #[arg(short, long, default_value = "http://localhost:8985/solr/biblio/select")]
    solr_url: String,

    /// Nombre del archivo de salida
    #[arg(short, long, default_value = "backup_solr.json")]
    output_file: String,

    /// Cantidad de documentos por lote
    #[arg(short, long, default_value_t = 1000)]
    batch_size: usize,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 0. Parsear los argumentos de la línea de comandos
    let args = Args::parse();
    
    let client = Client::new();

    // 1. Obtener el total de documentos (Usamos args.solr_url)
    let initial_resp: Value = client.get(&args.solr_url)
        .query(&[("q", "*:*"), ("rows", "0"), ("wt", "json")])
        .send()?.json()?;
    
    let total_docs = initial_resp["response"]["numFound"].as_u64().unwrap_or(0);

    // 2. Configurar la barra de progreso
    let pb = ProgressBar::new(total_docs);
    pb.set_style(ProgressStyle::default_bar()
        .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")?
        .progress_chars("#>-"));

    // 3. Preparar archivo (Usamos args.output_file)
    let file = File::create(&args.output_file)?;
    let mut writer = BufWriter::new(file);
    writer.write_all(b"[\n")?;

    let mut cursor_mark = "*".to_string();
    let mut is_first_batch = true;

    // 4. Bucle de exportación
    loop {
        let response: Value = client.get(&args.solr_url)
            .query(&[
                ("q", "*:*"),
                ("rows", &args.batch_size.to_string()), // Usamos args.batch_size
                ("sort", "id asc"), 
                ("cursorMark", &cursor_mark),
                ("wt", "json")
            ])
            .send()?
            .json()?;

        let docs = response["response"]["docs"].as_array().ok_or("Error al leer docs")?;
        let next_cursor_mark = response["nextCursorMark"].as_str().ok_or("No se encontró nextCursorMark")?;

        if docs.is_empty() { break; }

        for (i, doc) in docs.iter().enumerate() {
            if !is_first_batch || i > 0 {
                writer.write_all(b",\n")?;
            }
            serde_json::to_writer(&mut writer, &doc)?;
        }

        pb.inc(docs.len() as u64);

        if cursor_mark == next_cursor_mark { break; }
        cursor_mark = next_cursor_mark.to_string();
        is_first_batch = false;
    }

    writer.write_all(b"\n]")?;
    writer.flush()?;
    
    pb.finish_with_message("Backup completado");
    println!("\nArchivo guardado: {}", args.output_file);
    
    Ok(())
}

Cómo usar los nuevos parámetros

Ahora puedes llamar a tu script de diferentes maneras desde la terminal:

  1. Usar los valores por defecto:
rust-script nombre_del_script.rs
  1. Cambiar solo la URL:
rust-script nombre_del_script.rs --solr-url "http://otro-servidor:8983/solr/core/select"
  1. Cambiar todos los parámetros (usando las versiones cortas -s, -o, -b):
rust-script nombre_del_script.rs -s "http://localhost:8983/solr/prod/select" -o "produccion.json" -b 5000
  1. Ver la ayuda:
rust-script nombre_del_script.rs --help

Cambios principales realizados:

  • **Dependencia clap**: Se añadió en el bloque cargo para gestionar los argumentos.
  • **Estructura Args**: Define qué parámetros acepta el script. He usado #[arg(short, long, default_value = "...")] para que sean opcionales y tengan un valor base si el usuario no los provee.
  • Acceso a variables: En lugar de variables locales fijas, ahora usamos args.solr_url, args.output_file y args.batch_size.

¿Te gustaría que añadiera alguna validación extra, como verificar que la URL empiece por "http" antes de intentar la conexión?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment