typestar

config_check.rs en Rust

Un pipeline de validación: parsear texto clave-valor, juntar cada problema, reportar.

use std::collections::HashMap;
use std::error::Error;
use std::fmt;

#[derive(Debug)]
enum Invalid {
    Missing(&'static str),
    NotANumber { key: &'static str, got: String },
    OutOfRange { key: &'static str, got: i64 },
}

impl fmt::Display for Invalid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Invalid::Missing(key) => write!(f, "{key} is required"),
            Invalid::NotANumber { key, got } => {
                write!(f, "{key} should be a number, got {got:?}")
            }
            Invalid::OutOfRange { key, got } => {
                write!(f, "{key} is out of range: {got}")
            }
        }
    }
}

fn parse(raw: &str) -> HashMap<&str, &str> {
    raw.lines()
        .filter_map(|line| line.trim().split_once('='))
        .map(|(k, v)| (k.trim(), v.trim()))
        .collect()
}

fn number(
    fields: &HashMap<&str, &str>,
    key: &'static str,
    range: std::ops::RangeInclusive<i64>,
) -> Result<i64, Invalid> {
    let raw = fields.get(key).ok_or(Invalid::Missing(key))?;
    let value: i64 = raw.parse().map_err(|_| Invalid::NotANumber {
        key,
        got: raw.to_string(),
    })?;
    if !range.contains(&value) {
        return Err(Invalid::OutOfRange { key, got: value });
    }
    Ok(value)
}

fn check(raw: &str) -> Vec<Invalid> {
    let fields = parse(raw);
    let mut problems = Vec::new();

    if !fields.contains_key("host") {
        problems.push(Invalid::Missing("host"));
    }
    for (key, range) in [("port", 1..=65535), ("workers", 1..=64)] {
        if let Err(e) = number(&fields, key, range) {
            problems.push(e);
        }
    }
    problems
}

fn main() -> Result<(), Box<dyn Error>> {
    let raw = "host = typestar.io\nport = 99999\nworkers = many";
    let problems = check(raw);

    if problems.is_empty() {
        println!("config looks good");
        return Ok(());
    }

    println!("{} problem(s):", problems.len());
    for p in &problems {
        println!("  - {p}");
    }
    Ok(())
}

Cómo funciona

  1. Un solo enum de error cubre las tres formas en que un campo puede fallar.
  2. La validación reúne todas las fallas en vez de parar en la primera.
  3. main devuelve Result, así que un error fatal de parseo sale con código distinto de cero.

Palabras clave y builtins usados aquí

El intento, en números

Líneas
78
Caracteres a escribir
1704
Tokens
554
Ritmo de tres estrellas
110 tpm

Al ritmo de tres estrellas de 110 tokens por minuto, este intento toma unos 302 segundos.

Escribe este fragmento

Paso 1 de 1 en Bis; paso 15 de 15 en Manejo de errores.

← Anterior