# Règles Rust générales ## Portée Les règles `RUST-*` s'appliquent à tous les fichiers Rust de KSP : crates, sources, tests, exemples et outils. Elles reprennent les règles déjà éprouvées sur les générations précédentes du projet et les rendent explicites afin que `rustfmt` et Clippy ne soient jamais considérés comme des contrôles structurels suffisants. ## Édition et lints - **RUST-BASE-001** — L'édition Rust est Rust 2024, sauf contrainte externe explicitement documentée. - **RUST-BASE-002** — Chaque `lib.rs` et `main.rs` contient `#![warn(missing_docs)]`, `#![deny(unreachable_pub)]` et `#![forbid(unsafe_code)]`. - **RUST-BASE-003** — Les lints communs sont déclarés au niveau workspace et hérités par les crates. - **RUST-BASE-004** — Le code `unsafe` est interdit. - **RUST-BASE-005** — Tout fichier Rust possède les en-têtes `// file: ...` et `// version: N`, et se termine par exactement une fin de ligne. ## Documentation des API et contrats internes - **RUST-DOC-001** — Tout élément `pub` ou `pub(crate)` possède une rustdoc utile au point de déclaration. Cette règle couvre aussi les méthodes associées et les champs nommés visibles à ces niveaux. - **RUST-DOC-002** — Toute réexportation `pub use` ou `pub(crate) use` dans `lib.rs` ou `main.rs` possède sa propre rustdoc utile et adjacente. - **RUST-DOC-003** — La façade d'une crate doit permettre de comprendre son API publique et ses contrats crate-wide sans dépendre de chemins de modules internes. - **RUST-DOC-004** — Les rustdocs et commentaires du code sont en anglais ; la documentation Markdown KSP reste en français sauf document explicitement destiné à une audience différente. ## Imports et chemins - **RUST-IMPORT-001** — `use` est interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros. - **RUST-IMPORT-002** — `use` est autorisé uniquement pour un trait lorsqu'une résolution de méthode, une dérivation ou une contrainte du langage l'exige réellement. Le motif est explicité sur la ligne par `rust-rules: trait-import`. - **RUST-IMPORT-003** — Les imports de traits restent étroits ; un import de trait ne mélange jamais d'éléments non-traits. - **RUST-IMPORT-004** — Les glob imports et imports groupés par accolades sont interdits. - **RUST-IMPORT-005** — Les alias `as` sont interdits dans tous les `use`, réexports et déclarations `extern crate`. Le symbole reçoit son nom canonique dans son module propriétaire. - **RUST-IMPORT-006** — Les déclarations `use` sont au niveau du module, dans le bloc d'en-tête avant les déclarations métier. Aucun `use` n'est introduit dans une fonction, méthode, bloc, test ou branche conditionnelle. - **RUST-IMPORT-007** — Pour un élément fourni par une crate externe, utiliser directement le chemin public le plus court exposé par cette crate ; ne pas créer un import non-trait pour raccourcir ce chemin. - **RUST-IMPORT-008** — Pour un élément `pub` d'une crate KSP, la crate propriétaire le réexporte au crate-root. Une autre crate l'appelle via `owner_crate::Item`. - **RUST-IMPORT-009** — Dans sa propre crate, un élément `pub` ou `pub(crate)` partagé est appelé via `crate::Item`, y compris depuis son module de déclaration lorsque le contrat est crate-wide. - **RUST-IMPORT-010** — Un chemin `crate::module::Item` est interdit pour un élément partagé `pub`/`pub(crate)` qui peut être consommé via le crate-root. Le chemin du module interne n'est pas une façade. - **RUST-IMPORT-011** — Un élément strictement privé à un module n'est pas réexporté et est appelé par son nom local dans ce module. - **RUST-IMPORT-012** — Dans un sous-module de tests, `super::Item` est réservé à un élément strictement privé du module parent. Un élément `pub` ou `pub(crate)` continue d'être appelé via `crate::Item`. - **RUST-IMPORT-013** — Les réexports internes commencent par `self::`. Un réexport d'une crate externe peut utiliser directement le chemin externe canonique. - **RUST-IMPORT-014** — Un export correspond à une ligne de réexport distincte ; les accolades ne servent jamais à regrouper une façade. - **RUST-IMPORT-015** — Une crate externe rendue publique uniquement pour l'hygiène d'une macro exportée conserve son nom canonique, porte `#[doc(hidden)]` et ne devient pas une API de consommation. `ksp-logging-lib::tracing` est ce bridge technique pour les macros Logging ; les autres crates KSP n'y accèdent jamais directement. ## Visibilité et façade de crate - **RUST-API-001** — Aucun `pub mod` n'est autorisé ; les modules restent privés et l'API externe est constituée exclusivement par des réexports explicites au crate-root. - **RUST-API-002** — `pub(in ...)` et `pub(super)` sont interdits. - **RUST-API-003** — Un élément `pub` inaccessible depuis la façade de sa crate est une erreur de conception. - **RUST-API-004** — Un élément `pub(crate)` consommé hors de son module est réexporté au crate-root via `pub(crate) use` puis appelé via `crate::Item`. - **RUST-API-005** — Les chemins internes de modules ne constituent jamais une API stable. - **RUST-API-006** — Si deux éléments crate-wide auraient le même nom au crate-root, ils sont renommés dans leurs modules propriétaires avec des noms canoniques non ambigus ; un alias de réexport n'est pas utilisé pour masquer la collision. ## Formatage, blocs et ordre - **RUST-FMT-001** — `rustfmt.toml` à la racine est la configuration canonique du formatage Rust. - **RUST-FMT-002** — `cargo fmt --all` est exécuté après chaque modification Rust avant l'audit structurel et les validations Cargo. - **RUST-FMT-003** — Aucune ligne vide n'est conservée à l'intérieur du corps d'une fonction ou méthode, ni à l'intérieur d'une définition `struct` ou `enum`. - **RUST-FMT-004** — Les lignes vides séparent les fonctions/méthodes, types, blocs `impl` et grandes sections logiques ; elles ne fragmentent pas un bloc homogène de déclarations. - **RUST-FMT-005** — Les blocs homogènes sont ordonnés alphabétiquement par nom lorsque l'ordre n'a pas de signification sémantique. - **RUST-FMT-006** — Les constantes de module forment des blocs sans ligne vide interne, ordonnés par visibilité `pub`, puis `pub(crate)`, puis privée, et alphabétiquement dans chaque niveau. - **RUST-FMT-007** — Dans une façade, le bloc `pub use` précède le bloc `pub(crate) use`; une seule ligne vide sépare les deux blocs, aucune ligne vide n'existe à l'intérieur d'un bloc, et les symboles sont ordonnés alphabétiquement. - **RUST-FMT-008** — Les déclarations `mod` d'un même bloc sont ordonnées alphabétiquement ; les modules de tests conditionnels restent après les modules de production. - **RUST-FMT-009** — Lorsqu'un `struct` et ses blocs `impl` forment une unité locale sans contrainte de séparation, l'implémentation suit la structure. Les éléments visibles précèdent les helpers privés lorsqu'aucun ordre métier ou protocolaire n'impose l'inverse. - **RUST-FMT-010** — L'ordre alphabétique n'écrase jamais un ordre contractuel ou sémantique : wire fields, comptes Solana, étapes de protocole, priorités, transitions d'état, tableaux de dispatch et séquences explicitement normatives conservent leur ordre défini. ## Contrôle de flux et erreurs - **RUST-ERR-001** — `unwrap`, `expect` et `panic` sont interdits dans le code de production. - **RUST-ERR-002** — L'opérateur `?` est interdit dans le code de production ; les chemins d'erreur utilisent un contrôle de flux explicite. - **RUST-ERR-003** — Les retours sont explicites conformément au lint `clippy::implicit_return`. - **RUST-ERR-004** — `anyhow` et `thiserror` ne sont pas utilisés par défaut ; leur introduction exige une justification architecturale. - **RUST-ERR-005** — Les erreurs publiques sont typées lorsque leur contrat est stable. - **RUST-ERR-006** — Les tests peuvent utiliser `unwrap` ou `expect` uniquement dans la limite explicitement autorisée par la configuration Clippy. - **RUST-ERR-007** — Avant d'attacher une erreur externe comme `source` de `ksp_core_lib::Error`, la crate propriétaire vérifie que sa chaîne `Debug`/`source` ne peut pas exposer de secret. Lorsqu'une dépendance fournit une primitive de neutralisation, elle est appliquée avant `with_source`. ## Helpers et duplication - **RUST-HELP-001** — Un helper répété dans plusieurs modules d'une même crate est déplacé dans un module commun dont le nom décrit la responsabilité la plus précise possible. - **RUST-HELP-002** — Un helper réellement général et réutilisable par plusieurs crates est déplacé dans la bibliothèque KSP appropriée. - **RUST-HELP-003** — La mutualisation ne doit pas créer de dépendance cyclique ni déplacer une logique métier spécifique dans une bibliothèque générique. ## Dépendances - **RUST-DEP-001** — Une dépendance n'est ajoutée que si elle est réellement utilisée dans le chemin de compilation concerné. - **RUST-DEP-002** — Une dépendance uniquement utilisée par les tests reste dans `[dev-dependencies]`. - **RUST-DEP-003** — Les features sont minimales et explicites. - **RUST-DEP-004** — Les lockfiles de dépendances ne sont pas versionnés dans KSP. - **RUST-DEP-005** — KSP privilégie les versions récentes compatibles. Toute version volontairement contrainte ou ancienne est documentée avec sa raison et la condition permettant de lever la contrainte. ## Assertions et organisation des tests - **RUST-TEST-001** — Un test vérifiant uniquement l'appartenance à un ensemble peut trier les valeurs obtenues et attendues avant `assert_eq!` lorsque l'ordre n'est pas contractuel. - **RUST-TEST-002** — Une collection n'est jamais triée artificiellement lorsque l'ordre fait partie du contrat, notamment pour comptes Solana, metas, instructions, signers, événements, étapes de pipeline, priorités ou journaux ordonnés. - **RUST-TEST-003** — Les tests unitaires sont placés autant que possible dans `unit_tests/` et reflètent l'arborescence de `src/`. - **RUST-TEST-004** — Un fichier `unit_tests/...` est rattaché explicitement au module de production via `#[cfg(test)]` et `#[path = ...]`; il peut tester les éléments privés de ce module. - **RUST-TEST-005** — `tests/` est réservé aux vrais tests d'intégration consommant uniquement l'API publique de la crate. - **RUST-TEST-006** — Toute partie pertinente de l'API publique est couverte, lorsque possible, par un test d'intégration afin de valider les réexports et la visibilité réellement consommables. - **RUST-TEST-007** — La colocalisation d'un test unitaire dans le fichier de production reste une exception justifiée. ## Audit structurel automatisé - **RUST-AUDIT-001** — `scripts/audit_rust_workspace_rules.py` est le point d'entrée obligatoire de l'audit Rust KSP. Il exécute les audits généraux, la complétude des réexports/chemins et les frontières KSP sans fusionner leurs responsabilités. - **RUST-AUDIT-002** — L'audit est dependency-free côté Python standard et échoue avec un code non nul dès qu'une violation mécanique est détectée. - **RUST-AUDIT-003** — L'audit contrôle au minimum : headers/version/newline, lints de crate-root, visibilité interdite, `use`/aliases/groupes/globs/scope, rustdocs visibles, ordre des imports/réexports/constantes, lignes vides dans fonctions/structs/enums, complétude des réexports crate-root, chemins `crate::module::Item`, usage de `super::` dans les tests séparés et frontières KSP directement vérifiables. - **RUST-AUDIT-004** — Les règles contextuelles qui ne peuvent pas être prouvées sans interpréter la sémantique restent des critères de revue humaine ; le script ne doit pas produire de faux sentiment de complétude. ## Contrôle avant livraison Après toute modification Rust, la séquence minimale est : ```bash cargo fmt --all python3 scripts/audit_rust_workspace_rules.py cargo check --workspace cargo clippy --workspace --all-targets ``` Pendant le développement, des tests ciblés suivent ce gate. À la fermeture d'une prerelease technique/release, exécuter également : ```bash cargo test --workspace ``` Une commande non exécutée n'est jamais déclarée réussie. `rustfmt`, `cargo check` et Clippy ne remplacent pas l'audit structurel KSP.