code-simplifier

Par rtk-ai · rtk

Révision du code RTK Rust pour simplification idiomatique. Détecte le sur-engineering, les allocations inutiles et les patterns verbeux. Applique les idiomes Rust sans modifier le comportement.

npx skills add https://github.com/rtk-ai/rtk --skill code-simplifier

RTK Code Simplifier

Examen et simplification du code Rust dans RTK tout en respectant les contraintes du projet.

Contraintes (ne jamais simplifier)

  • lazy_static! regex — ne peut pas être déplacée dans les fonctions même si c'est "plus simple"
  • .context() sur chaque ? — verbeux mais obligatoire
  • Fallback vers commande brute — ne jamais supprimer même si cela semble du code mort
  • Propagation du code de sortie — ne jamais simplifier vers Ok(())
  • #[cfg(test)] mod tests — ne jamais supprimer les modules de test

Motifs de simplification

1. Chaînes d'itérateurs plutôt que boucles manuelles

// ❌ Verbeux
let mut result = Vec::new();
for line in input.lines() {
    let trimmed = line.trim();
    if !trimmed.is_empty() && trimmed.starts_with("error") {
        result.push(trimmed.to_string());
    }
}

// ✅ Idiomatique
let result: Vec<String> = input.lines()
    .map(|l| l.trim())
    .filter(|l| !l.is_empty() && l.starts_with("error"))
    .map(str::to_string)
    .collect();

2. Construction de chaînes

// ❌ Boucle de push verbeux
let mut out = String::new();
for (i, line) in lines.iter().enumerate() {
    out.push_str(line);
    if i < lines.len() - 1 {
        out.push('\n');
    }
}

// ✅ join
let out = lines.join("\n");

3. Chaînage Option/Result

// ❌ Match imbriqué
let result = match maybe_value {
    Some(v) => match transform(v) {
        Ok(r) => r,
        Err(_) => default,
    },
    None => default,
};

// ✅ Chaîné
let result = maybe_value
    .and_then(|v| transform(v).ok())
    .unwrap_or(default);

4. Destructuration de struct

// ❌ Accès répété aux champs
fn process(args: &MyArgs) -> String {
    format!("{} {}", args.command, args.subcommand)
}

// ✅ Destructurer
fn process(&MyArgs { ref command, ref subcommand, .. }: &MyArgs) -> String {
    format!("{} {}", command, subcommand)
}

5. Retours anticipés plutôt qu'imbrication

// ❌ Profondément imbriqué
fn filter(input: &str) -> Option<String> {
    if !input.is_empty() {
        if let Some(line) = input.lines().next() {
            if line.starts_with("error") {
                return Some(line.to_string());
            }
        }
    }
    None
}

// ✅ Retour anticipé
fn filter(input: &str) -> Option<String> {
    if input.is_empty() { return None; }
    let line = input.lines().next()?;
    if !line.starts_with("error") { return None; }
    Some(line.to_string())
}

6. Éviter les clones redondants

// ❌ Clone inutile
fn filter_output(input: &str) -> String {
    let s = input.to_string();  // Clone inutile
    s.lines().filter(|l| !l.is_empty()).collect::<Vec<_>>().join("\n")
}

// ✅ Travailler avec &str
fn filter_output(input: &str) -> String {
    input.lines().filter(|l| !l.is_empty()).collect::<Vec<_>>().join("\n")
}

7. Utiliser if let pour une correspondance mono-variante

// ❌ Match complet pour une variante
match output {
    Ok(s) => process(&s),
    Err(_) => {},
}

// ✅ if let (mais toujours gérer les erreurs dans RTK — ne pas les ignorer silencieusement)
if let Ok(s) = output {
    process(&s);
}
// Note : dans les filtres RTK, toujours gérer Err avec eprintln! + fallback

Vérifications RTK-Spécifiques

Exécutez-les après la simplification :

# Vérifier l'absence de régressions
cargo fmt --all && cargo clippy --all-targets && cargo test

# Vérifier qu'aucune regex n'a été ajoutée dans les fonctions
grep -n "Regex::new" src/<file>.rs
# Tous doivent être à l'intérieur des blocs lazy_static!

# Vérifier qu'aucun unwrap n'a été ajouté en production
grep -n "\.unwrap()" src/<file>.rs
# Ne devrait apparaître que dans les blocs #[cfg(test)]

Ce qu'il ne faut PAS simplifier

  • lazy_static! { static ref RE: Regex = Regex::new(...).unwrap(); } — le .unwrap() ici est acceptable, c'est l'initialisation
  • Chaînes .context("description")? — verbeux mais requis
  • Le bras de correspondance fallback Err(e) => { eprintln!(...); raw_output } — semble redondant mais c'est le filet de sécurité
  • std::process::exit(code) à la fin de run() — semble pouvoir être Ok(()) mais ce n'est pas le cas

Skills similaires