# Règles Rust générales Ce fichier contient les règles normatives applicables à tous les projets Rust. Elles sont indépendantes de `khadhroony-bot3` et doivent pouvoir être réutilisées telles quelles dans un autre workspace. ## En-têtes et versions de fichiers - Tout fichier texte qui supporte des commentaires commence par une ligne indiquant son chemin relatif dans le projet, puis une ligne `version` entière. - Un script exécutable nécessitant un shebang conserve celui-ci en première ligne ; les lignes `file:` et `version:` suivent immédiatement le shebang. - La version d'un fichier est incrémentée à chaque modification après validation de sa version précédente. - Les fichiers Rust utilisent `// file: ...` et `// version: N`. - Les fichiers Markdown utilisent `` et ``. - Tous les fichiers texte se terminent par exactement une fin de ligne. ## Langue et documentation - Les commentaires et rustdocs du code sont rédigés en anglais. - Les documents Markdown du projet sont rédigés dans la langue documentaire choisie par le projet. - Tout élément `pub` ou `pub(crate)` possède une rustdoc utile au point de déclaration. - Toute réexportation `pub use` ou `pub(crate) use` dans `lib.rs` ou `main.rs` possède également sa propre rustdoc copiée ou reformulée de manière équivalente. La documentation du point d'entrée doit permettre de comprendre l'API sans ouvrir le module interne. ## Édition et lints obligatoires - L'édition Rust cible est Rust 2024, sauf contrainte explicitement documentée. - Chaque `lib.rs` et `main.rs` contient : - `#![warn(missing_docs)]` ; - `#![deny(unreachable_pub)]` ; - `#![forbid(unsafe_code)]`. - Les lints Clippy obligatoires sont déclarés au niveau workspace et hérités par toutes les crates. - Les règles minimales sont : interdiction de `unwrap`, `expect`, `?`, retours implicites, code `unsafe`, APIs publiques inaccessibles et imports globaux non justifiés. - Les tests peuvent disposer d'exceptions limitées pour `unwrap` et `expect` uniquement lorsqu'elles sont explicitement autorisées par la configuration Clippy. La configuration workspace doit au minimum déclarer : ```toml [workspace.lints.rust] missing_docs = "warn" unreachable_pub = "deny" unsafe_code = "forbid" [workspace.lints.clippy] unwrap_used = "deny" expect_used = "deny" implicit_return = "deny" needless_return = "allow" useless_vec = "deny" question_mark = "deny" question_mark_used = "deny" needless_match = "allow" manual_ok_err = "allow" manual_unwrap_or = "allow" manual_map = "allow" match_like_matches_macro = "allow" single_match = "allow" manual_unwrap_or_default = "allow" manual_find = "allow" explicit_counter_loop = "allow" get_first = "allow" implicit_saturating_sub = "allow" ``` ## Formatage - `cargo fmt --all` est exécuté après application de chaque delta ou correctif et avant les tests. - Les fichiers Rust ne contiennent pas de lignes vides à l'intérieur d'une fonction, d'une structure, d'une énumération ou d'une implémentation courte. - Les lignes vides séparent uniquement les fonctions, blocs `impl`, types et sections logiques. - Les exports de `lib.rs` ou `main.rs` ne contiennent aucune ligne vide à l'intérieur d'une série homogène de `pub use` ou `pub(crate) use`. - Les séries `pub use` et `pub(crate) use` forment deux groupes séparés lorsqu'elles coexistent. - Le groupe `pub use` précède le groupe `pub(crate) use` dans une même façade. - L’ordre interne suit l’ordre naturel produit par `rustfmt` : les segments et mots séparés sont comparés avant leurs suffixes numériques, par exemple `U8` avant `U16` et `TOKEN` avant `TOKEN2022`. ## Assertions sur les collections - Un test qui vérifie uniquement l’appartenance à un ensemble peut trier la valeur obtenue et la valeur attendue avant `assert_eq!`, afin de ne pas dépendre d’un ordre d’enregistrement non contractuel. - Le tri doit être explicite dans le test et appliqué aux deux collections comparées. - Ne jamais trier une collection lorsque l’ordre fait partie du contrat : comptes Solana, metas, instructions, signers, événements, étapes de pipeline, priorités, journaux ou toute autre séquence ordonnée. ## Imports et chemins - `use` est interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros. - `use` est autorisé uniquement pour un trait lorsque la résolution de méthode, une macro de dérivation ou une contrainte de langage l'exige réellement. - Un import de trait doit rester étroit et ne doit jamais importer simultanément des éléments non-traits par accolades. - Les imports globaux, glob imports et imports groupés par accolades sont interdits. - Pour un élément fourni par une crate externe au workspace, utiliser directement le chemin public le plus court exposé par cette crate. Exemple : utiliser `external_crate::Struct`, jamais `external_crate::module::Struct` si `external_crate::Struct` existe, et jamais `use external_crate::Struct`. - Pour un élément `pub` déclaré dans le workspace : - il est réexporté depuis le `lib.rs` ou `main.rs` de sa crate propriétaire ; - depuis une autre crate, il est appelé via `owner_crate::Item` ; - depuis sa propre crate, il est appelé via `crate::Item`, même depuis son module de déclaration. - Pour un élément `pub(crate)` : - il est réexporté depuis le `lib.rs` ou `main.rs` via `pub(crate) use` ; - il est appelé via `crate::Item`, même depuis son module de déclaration. - Un élément strictement privé à un module n'est pas réexporté et est appelé par son nom local uniquement. Il est interdit d'utiliser un chemin long comme `crate::module::helper` ou `owner_crate::module::helper` pour un helper privé du module courant. - Dans un sous-module de tests, un élément privé du module parent est appelé via `super::Item`. Un élément `pub` ou `pub(crate)` continue d’être appelé via le point d’entrée de crate le plus court, par exemple `crate::Item`. - Les exports ne sont jamais groupés avec des accolades. Un type, une fonction, une constante ou un alias correspond à une ligne de réexport distincte. - Un réexport d’un module interne commence par `self::`, y compris dans `lib.rs` ; `use crate::...` est réservé aux références qualifiées dans le code et ne sert pas à construire une façade. - Les alias `as` sont interdits dans les réexports : le symbole reçoit son nom canonique dans son module propriétaire avant d’être réexporté sans transformation. - Un bloc homogène de `pub use` ou `pub(crate) use` ne contient aucune ligne vide et reste ordonné alphabétiquement par symbole réexporté lorsque `rustfmt` ne le réordonne pas. ## Visibilité et API de crate - Aucun `pub mod` n'est autorisé. Les modules restent privés et l'API est constituée exclusivement par des réexports explicites depuis le point d’entrée de la crate. - Un élément `pub` inaccessible depuis le point d'entrée de sa crate est une erreur de conception, pas un simple avertissement. - Un élément `pub(crate)` utilisé hors de son module est réexporté au niveau du point d'entrée de la crate. - Les chemins internes de modules ne font pas partie de l'API stable. - Les méthodes inhérentes publiques restent appelées via le type réexporté ; les fonctions libres publiques sont appelées via le point d'entrée de crate. ## Helpers et réutilisation - Un helper répété dans plusieurs modules d'une même crate est déplacé dans un module commun explicite, généralement `helper.rs` ou un module spécialisé plus précis. - Le nom `helpers.rs` ou `helper.rs` n'est utilisé que si aucune responsabilité métier plus précise ne convient. - Un helper général réutilisable par plusieurs crates est déplacé dans une crate commune appropriée et exposé publiquement. - Les types d'erreur généraux, identifiants de programme, primitives de validation et fonctions de sérialisation communes ne doivent pas être dupliqués entre crates. - La mutualisation ne doit pas créer de dépendance cyclique ni déplacer un comportement métier spécifique dans une crate générique. ## Gestion des erreurs et contrôle de flux - `unwrap`, `expect` et `panic` sont interdits dans le code de production. - L'opérateur `?` est interdit dans les chemins de production ; utiliser des `match` explicites avec erreurs contextualisées. - `anyhow` et `thiserror` ne sont pas utilisés par défaut. - Les erreurs publiques sont typées lorsque leur contrat est stable ; les diagnostics dynamiques restent bornés. - Les retours sont explicites conformément au lint `clippy::implicit_return`. ## Sécurité et dépendances - Le code `unsafe` est interdit. - Une dépendance n'est ajoutée que si elle est réellement utilisée dans le chemin de compilation concerné. - Une dépendance utilisée uniquement dans les tests reste dans `[dev-dependencies]`. - Les features sont minimales et explicites. ## Contrôle avant livraison Chaque livraison Rust exécute au minimum, dans cet ordre : ```bash cargo fmt --all python3 scripts/audit_rust_general_rules.py python3 scripts/audit_khadhroony_workspace_rules.py cargo test --workspace cargo clippy --workspace --all-targets ``` Un projet peut utiliser une sélection de tests plus étroite pendant le développement, mais la fermeture d'une version exige le contrôle global. Le wrapper `python3 scripts/audit_rust_workspace_rules.py` exécute les deux audits sans fusionner leurs responsabilités.