140 lines
8.7 KiB
Markdown
140 lines
8.7 KiB
Markdown
<!-- file: RUST_RULES.md -->
|
||
<!-- version: 5 -->
|
||
|
||
# Règles Rust générales
|
||
|
||
Ce fichier contient les règles normatives applicables à tous les projets Rust. Elles sont indépendantes de `khadhroony-bot2` 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.
|
||
- 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 `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`.
|
||
|
||
## 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.
|