encre-rust
Conventions Rust et WebAssembly du design system de Paul Perigault. À charger pour écrire ou relire un crate (`crates/pp-color`, `crates/pp-data`, crates `-wasm`), un `Cargo.toml`, `rust-toolchain.tom
SKILL.md.claude/skills/encre-rust/SKILL.md
GitHubEncre : conventions Rust et WebAssembly
Issue : #178. Sources du dépôt :
ADR-0006 (décision 4), ADR-0007 (parité de pp-data), ADR-0005, D-030, AGENTS.md, encre-securite.
Sources externes : section « Sources ». Ce skill est de la méthode, pas une preuve : « prouvé »
seulement si une source l'établit ; sinon « convention » ou « préférence ». Aucun crate n'existe
encore au 2026-10-30 : les règles ci-dessous sont celles de l'ADR, à appliquer dès l'étape 7a (#88).
Comment l'utiliser
- Phrase de déclenchement : « revue Rust de
<crate, PR ou fichier>» (ou/encre-rust). - Lire le code avant de juger ; citer
fichier:ligne. Sortie : Critique, À améliorer, Ce qui tient. - En cas d'écart avec ADR-0006 ou D-030, la source du dépôt gagne.
Architecture des crates (ADR-0006, décision 4)
- Espace de travail Cargo à la racine ;
crates/pp-color(OKLCH, sRGB, gamut, contraste WCAG 2 et APCA) etcrates/pp-data(agrégation, quantiles, sous-échantillonnage). - Un crate métier est du Rust pur, sans dépendance à
wasm-bindgen. La liaison WebAssembly vit dans un crate-wasmmince (conversion des types, aucune logique). Raison : un futur back Rust ou Go réutilise le crate sans le glue JavaScript.wasm-bindgenrelie Rust et JavaScript et peut générer des types TypeScript (guide wasm-bindgen). - Sens des dépendances comme côté TypeScript : le crate
-wasmdépend du crate métier, jamais l'inverse. Une logique dans un crate-wasmest une erreur de revue. - Logique pure, dépendances injectées, comme en TypeScript (
AGENTS.md). Les limites de taille du dépôt (fichier, fonction, complexité) s'appliquent au Rust ; on découpe, on ne relève rien. - Aucun nombre magique : constantes nommées (seuils, coefficients OKLCH), avec leur source en commentaire. Un coefficient de norme cite la norme.
Outillage bloquant
rust-toolchain.tomlépinglé. Contrôles (ADR-0006) :cargo fmt --check,cargo clippy -- -D warnings -W clippy::pedantic(pedantic en refus),cargo test,cargo audit,cargo deny.- Clippy classe
pedanticcomme groupe strict, à faux positifs assumés, où l'#[allow]ciblé est normal ;restrictionne s'active pas en bloc (documentation Clippy). Règle du dépôt : un#[allow]porte une raison écrite, sur l'élément le plus petit, jamais au niveau du crate. cargo auditlitCargo.lockcontre la base RustSec.cargo denyfait quatre contrôles :licenses,bans(crates interdits, doublons),advisories(failles, crates non maintenus ou retirés),sources(origine). Bloquants dès le premier crate, pas après.- Vérification locale :
cargo fmt --check,cargo clippyetcargo testsuffisent à la machine de Paul ;cargo auditetcargo denys'installent à part. S'ils manquent, le dire, ne pas les déclarer verts : la CI reste juge.
Aucun panic, erreurs typées
- Hors tests : ni
panic!,unwrap,expect,unreachable!,todo!, ni indexationv[i]non vérifiée. Utiliserget,first, les itérateurs,?. - Préférence de mise en œuvre : activer au niveau du crate
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::indexing_slicing)], avec#![cfg_attr(test, allow(...))]. Ces lints sont du grouperestriction: on les cite un par un, jamais en groupe. - Erreurs typées : un
enumpar crate (outhiserror), jamaisStringniBox<dyn Error>à l'interface publique. Les variantes décrivent la cause (OutOfGamut,InvalidHue,EmptyDataset), pas le message. - Validation aux bords : une fonction publique vérifie ses entrées (nombres finis, plages, longueurs)
et rend
Result; les API Guidelines rangent la vérification des arguments sous « Dependability ». Documenter# Errorspour chaqueResultpublique et# Panicssi un panic reste possible. - Un crate
-wasmconvertit l'erreur du crate métier en erreur de frontière ; il ne la perd pas.
unsafe interdit (règle validée par Paul)
#![forbid(unsafe_code)]en tête de chaque crate,-wasmcompris.forbidne se contourne pas par unallowlocal, ce quedenypermet (documentation rustc).- Une exception demande ⚖️ de Paul, avant le code : le bloc
unsafeporte un commentaire qui énonce le contrat. Leforbiddu crate ne s'abaisse pas : il devientdeny, et un#[allow(unsafe_code)]ne couvre que le module concerné, dans la même PR.--cap-lintspeut aussi abaisser unforbid. - Une dépendance qui contient de l'
unsafese juge à la revue de dépendance (mainteneur, usage, poids), pas à la règle ci-dessus, qui ne vise que le code du dépôt.
Tests : unitaires, propriétés, parité
- Une brique = un test au minimum (
AGENTS.md). Tests unitaires à côté du code ; tests d'intégration danstests/. proptestpour les propriétés : une propriété vérifie une règle sur beaucoup d'entrées générées et réduit (shrinking) toute entrée en échec à un cas minimal (proptest book). Propriétés prévues par l'ADR : aller-retour OKLCH, bornes du gamut. Les fichiersproptest-regressions/se commitent : ils rejouent les échecs passés (proptest book).- Parité avec la référence TypeScript, bloquante en CI : le TypeScript reste la référence tant que la
parité n'est pas établie. Mêmes vecteurs d'entrée des deux côtés (fichier de vecteurs partagé).
pp-color: écart ≤ 1e-6 sur les composantes, ratio de contraste arrondi identique (ADR-0006).pp-data: écart ≤ 1e-9 en relatif (ADR-0007), confirmé en V4a (D-097) : écart mesuré nul sur les quantiles et l'agrégation (D-098), indices LTTB identiques.
- Aucun gain de performance n'est exigé, aucune régression n'est tolérée (D-030) : temps de chargement mesuré avant et après, non dégradé.
Le .wasm : poids, provenance, build
- Compilé en CI, jamais commité. Publié dans le paquet npm (
@paulperigault/color, puis celui depp-data) avec la provenance npm ; même chaîne que les autres paquets (voirencre-securite). - Provenance : empreinte SHA-256 du
.wasmenregistrée par le build, SBOM de la release qui couvre aussi les crates (Cargo.locky entre). Empreinte et SBOM : mise en œuvre à décider à l'étape 8 (#90) ; ne pas déclarer cette provenance acquise avant. - Poids budgété et signalé (mesure affichée, voir
encre-greenops) ; un seuil ne bloque que si Paul le pose. Le dire dans la PR : poids avant, poids après. - Build Cloudflare sans Rust : ni
rustupnicargo; il consomme le paquet publié. Relire : aucune étape debuilddu site n'appellecargoouwasm-pack. - Le WebAssembly du site s'exécute au build (Node) (ADR-0007) ; côté navigateur, la CSP n'autorise que
'wasm-unsafe-eval', jamais'unsafe-eval'(voirencre-securite).
Machine de Paul
- Chaîne
windows-gnu(x86_64-pc-windows-gnu) ;cargoest dans~/.cargo/bin. Les chemins et fins de ligne diffèrent de Linux : ne jamais coder un chemin ni un séparateur en dur. - La CI Linux est la référence. Un test vert en local sans la CI ne prouve rien (flottants,
libm, fins de ligne des vecteurs). Vérifier d'abordcargo testen local, conclure avec la CI.
Relire une PR de crate
#![forbid(unsafe_code)]présent ? Aucunwasm-bindgendans un crate métier ?- Un
unwrap,expect,panic!,v[i]hors test ? Un#[allow]sans raison ? - Erreurs typées à l'interface publique,
# Errorsdocumenté ? - Parité et propriétés testées, vecteurs partagés, seuil cité ?
.wasm: poids avant et après, build Cloudflare sans Rust, aucun binaire commité ?cargo fmt,clippy,audit,denyverts ; sinon dire lesquels n'ont pas tourné.
Sources
Consultées le 2026-10-30. Niveaux : norme, étude, convention, préférence.
| Source | URL | Niveau |
|---|---|---|
| Rust API Guidelines, checklist | https://rust-lang.github.io/api-guidelines/checklist.html | convention |
| Documentation Clippy, groupes de lints | https://doc.rust-lang.org/clippy/lints.html | convention |
| Guide wasm-bindgen (nouvelle adresse) | https://wasm-bindgen.github.io/wasm-bindgen/ | convention |
| The Proptest Book, introduction | https://proptest-rs.github.io/proptest/intro.html | convention |
| Proptest, persistance des échecs | https://proptest-rs.github.io/proptest/proptest/failure-persistence.html | convention |
RustSec et cargo audit |
https://rustsec.org/ | convention |
| cargo-deny, les quatre contrôles | https://embarkstudios.github.io/cargo-deny/checks/index.html | convention |
rustc, niveaux de lint (forbid/deny) |
https://doc.rust-lang.org/rustc/lints/levels.html | norme |
L'ancienne adresse rustwasm.github.io/docs/wasm-bindgen/ ne sert plus que l'avis de déménagement.
Le guide wasm-bindgen ne décrit pas le glue JavaScript sur la page lue : l'affirmation est limitée
aux types TypeScript. Les quatre lints (unwrap_used, expect_used, panic, indexing_slicing)
sont dans restriction selon la réponse de l'outil de lecture, pas selon le texte de la page Clippy,
qui ne les liste pas : [À VÉRIFIER] dans la liste des lints Clippy.
Aucun texte n'est copié : tout est paraphrasé. Aucune étude citée : les seuils 1e-6 et 1e-9 sont
des choix de l'ADR, pas des résultats externes.