Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/check.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down Expand Up @@ -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
Expand Down
152 changes: 152 additions & 0 deletions concepts/analyzers/rust.mdx
Original file line number Diff line number Diff line change
@@ -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.
29 changes: 22 additions & 7 deletions concepts/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -75,21 +75,33 @@ 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
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,
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
Expand Down Expand Up @@ -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. |
Expand All @@ -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

Expand Down
8 changes: 6 additions & 2 deletions concepts/rule-packs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -79,4 +79,8 @@ alias target for an activated identity.
Framework-independent TypeScript policy and a standalone SvelteKit policy
with transparent TypeScript aliases.
</Card>
<Card title="Rust" icon="gear" href="/concepts/analyzers/rust">
Native Cargo workspace analysis with 117 layout, dependency, naming, shape,
hygiene, and test-convention rules.
</Card>
</CardGroup>
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -51,6 +51,7 @@
{
"group": "Additional Analyzers",
"pages": [
"concepts/analyzers/rust",
"concepts/analyzers/typescript-sveltekit"
]
}
Expand Down
12 changes: 9 additions & 3 deletions index.mdx
Original file line number Diff line number Diff line change
@@ -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."
---

<p className="text-right text-sm italic text-gray-500 dark:text-gray-400">
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
Loading