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

14 KiB

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-001use est interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros.
  • RUST-IMPORT-002use 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-002pub(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-001rustfmt.toml à la racine est la configuration canonique du formatage Rust.
  • RUST-FMT-002cargo 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-001unwrap, 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-004anyhow 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-005tests/ 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-001scripts/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 :

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 :

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.