Files
khadhroony-solana-project/docs/rules/RULES_RUST.md

128 lines
14 KiB
Markdown

<!-- file: docs/rules/RULES_RUST.md -->
<!-- version: 10 -->
# 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 fichier `unit_tests/...` rattaché au module parent, tout élément strictement privé du parent est appelé explicitement via `super::Item`. Inversement, un élément `pub` ou `pub(crate)` n'est jamais appelé via `super::` ni par un nom nu : il continue d'être appelé via le crate-root `crate::Item`, y compris lorsque le test est rattaché à son module de déclaration.
- **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.
- **RUST-API-007** — La visibilité d'un item n'est jamais élargie uniquement pour permettre son test. Un helper utilisé seulement par son module et ses `unit_tests/` reste privé et les tests y accèdent via `super::Item`; il ne devient `pub(crate)` que lorsqu'un autre module de production le consomme réellement, auquel cas `RUST-API-004` et `RUST-IMPORT-009` s'appliquent.
- **RUST-API-008** — Un helper strictement privé dont aucun chemin de production ne dépend encore et qui n'existe que pour un `unit_tests/` de préparation est compilé sous `#[cfg(test)]` jusqu'à sa première consommation de production réelle. Il ne reste pas mort dans le build normal et n'est pas conservé au moyen d'un `#[allow(dead_code)]` compensatoire.
## 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** — Deux fonctions ou méthodes distinctes, y compris deux `const fn`, sont séparées par exactement une ligne vide. La même séparation exacte s'applique entre `struct`, `enum`, `union`, `trait`, bloc `impl` et fonction/méthode lorsqu'ils constituent des items voisins d'un même scope.
- **RUST-FMT-005** — Les blocs homogènes sont ordonnés alphabétiquement par nom lorsque l'ordre n'a pas de signification sémantique. Cette règle ne redéfinit pas l'ordre interne des `use`/réexports : pour ceux-ci, la sortie de `cargo fmt --all` est canonique.
- **RUST-FMT-006** — Les constantes de module ou associées sont ordonnées par visibilité `pub`, puis `pub(crate)`, puis privée, et alphabétiquement dans chaque niveau. Deux constantes de même visibilité appartenant au même bloc ne sont séparées par aucune ligne vide ; exactement une ligne vide sépare deux niveaux de visibilité distincts.
- **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 et aucune ligne vide n'existe à l'intérieur d'un bloc. L'ordre intra-bloc des `use`/réexports est celui produit par `cargo fmt --all`; l'audit Python ne lui substitue aucun tri concurrent par symbole exporté ou chemin source.
- **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.
- **RUST-FMT-011** — Les blocs homogènes de `type` ou `static` de même visibilité suivent la même règle d'espacement compact que les constantes ; un changement de catégorie ou de visibilité recrée une séparation d'exactement une ligne vide.
- **RUST-FMT-012** — Une déclaration d'item placée après un `return` inconditionnel au niveau principal d'une fonction/méthode est interdite ; elle est traitée comme un signal probable d'accolade fermante déplacée ou de bloc mal restructuré.
## 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, structure des blocs de réexports, ordre des imports de traits et constantes lorsque KSP le possède, lignes vides dans fonctions/structs/enums, complétude des réexports crate-root, chemins `crate::module::Item`, accès `super::PrivateItem` et `crate::VisibleItem` dans les tests séparés, ainsi que les frontières KSP directement vérifiables. L'ordre intra-bloc des `use`/réexports reste la responsabilité canonique de `cargo fmt --all` et n'est pas réimplémenté par le script.
- **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.