typestar

fetch_tours.rs en Rust

Un CLI real: clap parsea los flags, reqwest descarga, serde modela la respuesta.

use std::error::Error;
use std::time::Duration;

use clap::Parser;
use serde::Deserialize;

/// Fetch the tour list for a language.
#[derive(Parser, Debug)]
#[command(name = "fetch-tours", version)]
struct Cli {
    /// Language slug, such as rust or sql
    lang: String,

    /// Base URL of the server
    #[arg(long, default_value = "https://typestar.io")]
    base: String,

    /// Only show tours with at least this many steps
    #[arg(long, default_value_t = 0)]
    min_steps: usize,

    /// Print the raw JSON as well
    #[arg(short, long)]
    verbose: bool,
}

#[derive(Debug, Deserialize)]
struct Paths {
    lang: String,
    tours: Vec<Tour>,
}

#[derive(Debug, Deserialize)]
struct Tour {
    slug: String,
    title: String,
    sections: Vec<Section>,
}

#[derive(Debug, Deserialize)]
struct Section {
    title: String,
    steps: Vec<Step>,
}

#[derive(Debug, Deserialize)]
struct Step {
    idx: u32,
    title: String,
}

impl Tour {
    fn step_count(&self) -> usize {
        self.sections.iter().map(|s| s.steps.len()).sum()
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let cli = Cli::parse();

    let client = reqwest::Client::builder()
        .timeout(Duration::from_secs(15))
        .user_agent("fetch-tours/1.0")
        .build()?;

    let url = format!("{}/api/paths?lang={}", cli.base, cli.lang);
    let response = client.get(&url).send().await?.error_for_status()?;
    let body = response.text().await?;

    if cli.verbose {
        println!("{} bytes from {url}", body.len());
    }

    let paths: Paths = serde_json::from_str(&body)?;
    println!("{} has {} tours", paths.lang, paths.tours.len());

    for tour in paths.tours.iter().filter(|t| t.step_count() >= cli.min_steps) {
        println!("{:<12} {:<32} {:>3} steps", tour.slug, tour.title,
                 tour.step_count());
        if cli.verbose {
            for section in &tour.sections {
                println!("    {} ({})", section.title, section.steps.len());
            }
        }
    }

    let widest = paths.tours.iter().max_by_key(|t| t.step_count());
    if let Some(tour) = widest {
        println!("largest tour: {}", tour.title);
    }
    Ok(())
}

Cómo funciona

  1. El struct Cli es toda la interfaz, texto de ayuda incluido.
  2. Los modelos espejan la respuesta de la API que la herramienta consume.
  3. Los errores de tres crates desembocan todos en Box<dyn Error>.

Palabras clave y builtins usados aquí

El intento, en números

Líneas
93
Caracteres a escribir
1932
Tokens
486
Ritmo de tres estrellas
115 tpm

Al ritmo de tres estrellas de 115 tokens por minuto, este intento toma unos 254 segundos.

Escribe este fragmento

Paso 1 de 1 en Bis; paso 12 de 12 en Herramientas CLI y clientes HTTP.

← Anterior