9.3 KiB
9.3 KiB
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
versionentière. - 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
<!-- file: ... -->et<!-- version: N -->. - 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
puboupub(crate)possède une rustdoc utile au point de déclaration. - Toute réexportation
pub useoupub(crate) usedanslib.rsoumain.rspossè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.rsetmain.rscontient :#![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, codeunsafe, APIs publiques inaccessibles et imports globaux non justifiés. - Les tests peuvent disposer d'exceptions limitées pour
unwrapetexpectuniquement lorsqu'elles sont explicitement autorisées par la configuration Clippy.
La configuration workspace doit au minimum déclarer :
[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 --allest 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.rsoumain.rsne contiennent aucune ligne vide à l'intérieur d'une série homogène depub useoupub(crate) use. - Les séries
pub useetpub(crate) useforment deux groupes séparés lorsqu'elles coexistent. - Le groupe
pub useprécède le groupepub(crate) usedans 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 exempleU8avantU16etTOKENavantTOKEN2022.
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
useest interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros.useest 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, jamaisexternal_crate::module::Structsiexternal_crate::Structexiste, et jamaisuse external_crate::Struct. - Pour un élément
pubdéclaré dans le workspace :- il est réexporté depuis le
lib.rsoumain.rsde 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.
- il est réexporté depuis le
- Pour un élément
pub(crate):- il est réexporté depuis le
lib.rsoumain.rsviapub(crate) use; - il est appelé via
crate::Item, même depuis son module de déclaration.
- il est réexporté depuis le
- 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::helperouowner_crate::module::helperpour 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émentpuboupub(crate)continue d’être appelé via le point d’entrée de crate le plus court, par exemplecrate::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 danslib.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
assont 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 useoupub(crate) usene contient aucune ligne vide et reste ordonné alphabétiquement par symbole réexporté lorsquerustfmtne le réordonne pas.
Visibilité et API de crate
- Aucun
pub modn'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
pubinaccessible 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.rsou un module spécialisé plus précis. - Le nom
helpers.rsouhelper.rsn'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,expectetpanicsont interdits dans le code de production.- L'opérateur
?est interdit dans les chemins de production ; utiliser desmatchexplicites avec erreurs contextualisées. anyhowetthiserrorne 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
unsafeest 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 :
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.