diff --git a/cli/check.mdx b/cli/check.mdx index 0494992..e148b98 100644 --- a/cli/check.mdx +++ b/cli/check.mdx @@ -29,7 +29,7 @@ explicit multi-target configuration runs every target and produces one report. | Argument | Description | | --- | --- | -| `paths...` | Optional. Use these paths instead of the configured `roots` for this run. | +| `paths...` | Optional for Python and web targets. Use these paths instead of configured `roots`. Rust targets use Cargo discovery and reject positional paths. | | `--warn` | Also evaluate and report rules configured in `warn`. Warnings never make the command fail. | | `--no-color` | Disable ANSI color in the output. | | `--cache` / `--no-cache` | Enable or disable persistent result caching for this run. | @@ -124,7 +124,7 @@ configuration. Both take rule-code **prefixes**: `FF` is every core rule, `FFR` family, `FFR3` a hundreds bucket, `FFR301` one rule; `X` is every custom rule, `XDB` a custom namespace, `XDB001` one custom rule. Activated native packs use the reserved `FP` namespace: `FPDG` selects the complete Dagster policy and `FPDG022` -selects one Dagster-native rule. To narrow a run, adjust those keys rather than the +selects one Dagster-native rule; `FPRS` selects the complete Rust policy. To narrow a run, adjust those keys rather than the command: ```toml diff --git a/concepts/analyzers/rust.mdx b/concepts/analyzers/rust.mdx new file mode 100644 index 0000000..7156b01 --- /dev/null +++ b/concepts/analyzers/rust.mdx @@ -0,0 +1,152 @@ +--- +title: Rust +description: "Configure native Cargo workspace analysis through fensu.toml and the normal Fensu CLI." +--- + +Fensu analyzes Rust projects natively. Cargo discovery, Rust parsing, workspace facts, +and all Rust rules are owned by the `fensu-rust` analyzer and run through the normal +`fensu` command. There is no separate Rust command or policy file. + +## Configure a workspace + +Add a named target to `fensu.toml`: + +```toml +[targets.rust] +analyzer = "rust" +roots = ["crates"] +tests = [] +tooling = [] +rule_packs = ["rust"] +select = ["FPRS"] +``` + +`rule_packs = ["rust"]` and `select = ["FPRS"]` are the Rust defaults. Keeping +them explicit makes the active policy clear in a multi-target repository. + +Then run: + +```bash +fensu check --target rust +``` + +For a single Cargo package, use `roots = ["src"]`. If the Cargo project is below +the repository root, set `root` to that directory. Target paths are relative to +`root`. + +Cargo discovers workspace members, package names, library and binary targets, and +integration-test targets automatically. You do not need to list crates unless you +deliberately want a closed inventory. Rust targets reject positional paths because +Cargo owns target discovery. + +## What it checks + +The `rust` pack exposes all 117 implemented rules under `FPRS`. The rules cover: + +- Cargo workspace and dependency policy; +- module ownership, visibility, layout, and import boundaries; +- file, function, naming, and error-handling hygiene; +- function shape and coordination budgets; and +- unit and integration-test structure and conventions. + +Use the normal selectors and policy tiers: + +```toml +select = ["FPRS"] +warn = [] +ignore = ["FPRSH001"] +``` + +Exact file exceptions and path-scoped ignores work as they do for other analyzers. +Rust exceptions name a Rust source file or `Cargo.toml`; they do not use Python +definition selectors. Inspect any rule and its effective settings with: + +```bash +fensu rule FPRSS010 --target rust +``` + +## Rule options + +Adjust a rule under the target's standard `rule_options` table: + +```toml +[targets.rust.rule_options.FPRSS010] +max_arguments = 8 + +[targets.rust.rule_options.FPRSR601] +max_file_lines = 1500 +``` + +The adjustable structural budgets retain these defaults: + +| Rule | Option | Default | +| --- | --- | --- | +| `FPRSR601` | `max_file_lines` | `2000` | +| `FPRSS010` | `max_arguments` | `10` | +| `FPRSS011` | `max_statements` | `70` | +| `FPRSS001` | `max_statements` | `40` | +| `FPRSS002` | `max_distinct_calls` | `20` | +| `FPRSS003` | `max_locals` | `20` | +| `FPRSR301` | `max_modules` | `10` | +| `FPRSR302` | `max_modules` | `20` | + +Budgets must be positive integers. Unknown rule codes, option names, and invalid +value types are rejected instead of being silently ignored. + +## Tooling and repository boundaries + +Use the target's normal `tooling` paths to identify tooling crates: + +```toml +[targets.rust] +analyzer = "rust" +roots = ["crates"] +tooling = ["crates/project-tools"] +rule_packs = ["rust"] +select = ["FPRS"] + +[targets.rust.rule_options.FPRSL301] +forbidden_packages = ["project-tools"] +``` + +Tooling paths receive the tooling-layout rules. `forbidden_packages` prevents runtime +crates outside those paths from depending on the named tooling packages and defaults +to an empty list. + +Parser boundaries are also explicit and repository-neutral by default: + +```toml +[targets.rust.rule_options.FPRSL102] +packages = ["syntax-parser"] +restricted_paths = ["rules"] +remediation = "consume shared fact models instead of parser types" +``` + +`packages` defaults to an empty list and `restricted_paths` defaults to `["rules"]`. +Dependency aliases are resolved when matching package identities. + +Closed inventories are optional: + +```toml +[targets.rust.rule_options.FPRSL304] +crate_names = ["app", "project-tools"] + +[targets.rust.rule_options.FPRSL305] +domain_paths = ["crates/app/src/orders"] +role_paths = ["crates/app/src/orders/main"] +intentional_layout_paths = ["crates/app/src/generated"] +``` + +Empty crate, domain, and role lists use automatic discovery. Configured paths must +be canonical repository-relative POSIX paths within the target root. Intentional-layout paths skip aggregate +layout checks for explicit subtrees while preserving per-file checks. + +## Cargo metadata and caching + +Rust sources, Cargo manifests, an existing lockfile, and target configuration are +cache inputs. Fensu uses locked dependency resolution when `Cargo.lock` exists. +For a lock-free workspace, it reads declared dependencies without creating a +lockfile. + +If Cargo metadata cannot be read, Fensu reports a setup diagnostic and does not cache +the result. The result remains uncached even when rule selection hides that diagnostic. diff --git a/concepts/configuration.mdx b/concepts/configuration.mdx index a4490c9..6e5eac9 100644 --- a/concepts/configuration.mdx +++ b/concepts/configuration.mdx @@ -57,7 +57,7 @@ max_positional_args = 1 ## Named analyzer targets The flat form above remains the concise configuration for one Python project. -Use explicit targets for multiple projects or a TypeScript/Svelte analyzer: +Use explicit targets for multiple projects or a Rust, TypeScript, or Svelte analyzer: ```toml [targets.backend] @@ -75,6 +75,14 @@ tests = ["tests"] test_layout = "mirrored" rule_packs = ["sveltekit"] select = ["FPSK"] + +[targets.rust] +analyzer = "rust" +roots = ["crates"] +tests = [] +tooling = ["crates/project-tools"] +rule_packs = ["rust"] +select = ["FPRS"] ``` Each target requires `analyzer` and `roots`. `root` defaults to `.` and relocates @@ -82,7 +90,7 @@ that target's paths relative to the configuration directory. Explicit `targets` cannot be mixed with legacy flat keys. `fensu check` evaluates every configured target unless `--target NAME` selects one. -The registered analyzers are `python`, `typescript`, and `svelte`. Web-only keys +The registered analyzers are `python`, `rust`, `typescript`, and `svelte`. Web-only keys include `test_layout`, `ui_kit`, `shadcn`, `openapi`, and `generated`; the normal policy, threshold, exception, evaluation, cache, and scope keys live inside each target too. The activated rule pack, rather than a separate framework setting, @@ -90,6 +98,10 @@ controls SvelteKit policy. See [TypeScript and SvelteKit](/concepts/analyzers/typescript-sveltekit) for detection, setup, and current boundaries. +Rust targets use Cargo to discover workspace members, package identities, and source targets. +They are configured entirely in `fensu.toml`; use ordinary `rule_options` for per-rule +adjustments. See [Rust](/concepts/analyzers/rust) for setup and the supported options. + ## Validation is strict Fensu fails loudly on a bad config rather than silently ignoring it, because a @@ -163,6 +175,7 @@ prefix of a valid code works: | `FP` | Every activated native-pack rule. | | `FPTS` | The complete activated TypeScript policy. | | `FPSK` | The complete activated SvelteKit policy. | +| `FPRS` | The complete activated Rust policy. | | `FPDG` | The complete activated Dagster policy. | | `FPDGA` | Dagster aliases of the core annotations family. | | `FPDG022` | Exactly one Dagster-native policy. | @@ -176,14 +189,16 @@ see [Rule families](/concepts/rule-families). `FP` is reserved for activated `X` plus optional uppercase letters plus optional digits, so namespaces nest by prefix (see [Custom rules](/concepts/custom-rules#codes-and-namespaces)). Web analyzers use the activated `FPTS` or `FPSK` catalogue described in -[TypeScript and SvelteKit](/concepts/analyzers/typescript-sveltekit). +[TypeScript and SvelteKit](/concepts/analyzers/typescript-sveltekit). Rust uses the +activated `FPRS` catalogue described in [Rust](/concepts/analyzers/rust). ### The default -`select` defaults to `["FF"]` for Python and `["FP"]` for TypeScript or Svelte. -For web targets, `FP` selects every activated pack. A web pack must be named in -`rule_packs` before its selectors become available; generated SvelteKit targets -activate `sveltekit` and select `FPSK` explicitly. +`select` defaults to `["FF"]` for Python and `["FP"]` for Rust, TypeScript, or Svelte. +For native targets, `FP` selects every activated pack. Rust targets activate the +`rust` pack automatically. Web packs must be named in `rule_packs` before their +selectors become available; generated SvelteKit targets activate `sveltekit` and +select `FPSK` explicitly. ### Run only some families diff --git a/concepts/rule-packs.mdx b/concepts/rule-packs.mdx index 427d747..7b1470b 100644 --- a/concepts/rule-packs.mdx +++ b/concepts/rule-packs.mdx @@ -35,8 +35,8 @@ pack's reference page. ## Codes and namespaces Fensu reserves `FP` for shipped native packs. Each pack owns a namespace beneath -that prefix. The Dagster, TypeScript, and SvelteKit packs own `FPDG`, `FPTS`, and -`FPSK`. +that prefix. The Dagster, Rust, TypeScript, and SvelteKit packs own `FPDG`, `FPRS`, +`FPTS`, and `FPSK`. Pack-native rules append their numeric identity directly, such as `FPDG022`. Retained core aliases preserve the core family and number after the pack namespace: @@ -79,4 +79,8 @@ alias target for an activated identity. Framework-independent TypeScript policy and a standalone SvelteKit policy with transparent TypeScript aliases. + + Native Cargo workspace analysis with 117 layout, dependency, naming, shape, + hygiene, and test-convention rules. + diff --git a/docs.json b/docs.json index cd7b353..eff12ec 100644 --- a/docs.json +++ b/docs.json @@ -2,7 +2,7 @@ "$schema": "https://mintlify.com/docs.json", "theme": "mint", "name": "Fensu", - "description": "Fensu enforces architectural boundaries, repository structure, code shape, and test conventions in Python projects.", + "description": "Fensu enforces architectural boundaries, repository structure, code shape, and test conventions across Python, Rust, TypeScript, and Svelte projects.", "markdown": { "instructions": [ "Read the Fensu Quickstart before onboarding a repository.", @@ -51,6 +51,7 @@ { "group": "Additional Analyzers", "pages": [ + "concepts/analyzers/rust", "concepts/analyzers/typescript-sveltekit" ] } diff --git a/index.mdx b/index.mdx index a34ea28..9b4f9e0 100644 --- a/index.mdx +++ b/index.mdx @@ -1,6 +1,6 @@ --- title: "Fensu (フェンス)" -description: "Keeping Python repos from turning into spaghetti." +description: "Executable architecture rules for Python, Rust, TypeScript, and Svelte repositories." ---

@@ -30,7 +30,7 @@ can tell you, deterministically and every time, when the repo has drifted from i ## What Fensu checks -Fensu is an architecture linter for Python repos. It analyzes your source and +Fensu is an architecture linter for Python, Rust, TypeScript, and Svelte projects. It analyzes your source and reports **faults**, the places where the code has broken away from the architecture you declared. @@ -61,12 +61,18 @@ dataclass-backed parametrization) and annotation conventions (annotate every parameter and return, plus non-scalar local bindings) in the scopes where those apply. See [Rule families](/concepts/rule-families) for the full set. +Python uses Fensu's core `FF` policy. Rust, TypeScript, SvelteKit, and framework-specific +Python projects use native rule packs selected per analyzer target. See +[Rust](/concepts/analyzers/rust), +[TypeScript and SvelteKit](/concepts/analyzers/typescript-sveltekit), and +[Native rule packs](/concepts/rule-packs). + Fensu ships a **coherent default architecture**, not a blank canvas. You adopt it and get a sensible starting point, then disable rules, tune thresholds, activate a framework-specific native policy, or add your own rules deliberately once you know what you are doing. -The default lays out code as domains built from a small set of roles. +The Python default lays out code as domains built from a small set of roles. A domain holds those roles directly, or splits into named subdomains that do: ```text