crap-core 0.4.0

Language-agnostic foundation for the CRAP analyzer family — domain types, port traits, and shared invariants for crap4rs / future crap4ts.
Documentation

crap-core

crates.io docs.rs License: MIT OR Apache-2.0

Language-agnostic foundation for the CRAP (Change Risk Anti-Patterns) analyzer family. Domain types, port traits, threshold and risk logic, reporters, and the shared scorecard envelope used by every CRAP adapter.

What this is

crap-core is the shared backbone for analyzers that compute CRAP scores —

CRAP(complexity, coverage) = complexity² × (1 − coverage)³ + complexity

— across different source languages. It owns:

  • The CRAP formula and four-tier risk classification (Low / Acceptable / Moderate / High)
  • The ComplexityPort and CoveragePort traits that language adapters implement
  • The locked wire envelope (scorecard, delta, crap-delta shapes) consumed by reporters and downstream tooling
  • All reporterstable, markdown, json, csv, sarif, scorecard, scorecard-row, github-annotations. HTML is on the roadmap
  • Threshold presets (strict / default / lenient), configuration parsing, and delta-gate semantics

If you're using crap-core directly, you're probably building a new language adapter. End users want one of the adapter crates instead:

  • crap4rs — Rust analyzer (syn complexity + LCOV coverage; cognitive complexity default)
  • crap4ts — TypeScript / JavaScript analyzer (oxc complexity + Istanbul JSON coverage; cyclomatic complexity)

Both link crap-core and produce byte-identical scorecard envelopes for the same (complexity, coverage) inputs — Rust and TypeScript CI gates stay consistent.

Install

[dependencies]
crap-core = "0.4"

Quick example

use crap_core::domain::crap::compute_crap;

let score = compute_crap(15, 0.90);
assert_eq!(format!("{:.2}", score), "15.23");

For the port traits and how adapters wire complexity + coverage data into a Scorecard, see the API docs on docs.rs.

Threshold presets and risk tiers

crap-core defines four risk tiers and three preset gates calibrated against them. Every adapter inherits this taxonomy automatically.

CRAP score Risk tier
≤ 8 Low
≤ 15 Acceptable
≤ 25 Moderate
> 25 High
Preset Threshold Gates at Use for
--strict CRAP ≤ 8 Low → Acceptable safety-critical, high-quality libraries
(default) CRAP ≤ 15 Acceptable → Moderate typical app / library code
--lenient CRAP ≤ 25 Moderate → High legacy / transitional codebases

The same preset values apply for both cognitive and cyclomatic complexity inputs (adapters choose which metric they pass into the formula).

What the output looks like

Every reporter consumes the same AnalysisView projection over the scored functions. Adapters never re-render shapes themselves — they hand crap-core the data and pick a --format.

Table — TTY default

crap4rs v0.5.0 — CRAP Score Analysis

+------------------------------------+----------------------------------+----+-------+-------+----------+
| File                               | Function                         | CC | Cov%  | CRAP  | Risk     |
+========================================================================================================+
| adapters/reporters/table.rs        | inject_breakdown_subrows         | 13 | 100.0 | 13.00 | moderate |
| domain/summary.rs                  | compute_summary                  | 13 | 100.0 | 13.00 | moderate |
| adapters/reporters/markdown.rs     | format_markdown_delta            | 12 |  97.1 | 12.00 | moderate |
+------------------------------------+----------------------------------+----+-------+-------+----------+

JSON — programmatic / cross-tool

{
  "schema_version": 2,
  "run_meta": { "tool": "crap4rs", "version": "0.5.0", "metric": "cognitive" },
  "result": {
    "summary": {
      "total_functions": 988,
      "exceeding_threshold": 0,
      "distribution": { "low": 951, "acceptable": 22, "moderate": 15, "high": 0 },
      "max_crap": { "value": 13.0, "risk_level": "moderate" }
    },
    "functions": [
      {
        "scored": {
          "identity": { "file_path": "src/lib.rs", "qualified_name": "process_request", "span": { "start_line": 42, "end_line": 87 } },
          "complexity": 11,
          "complexity_metric": "cognitive",
          "coverage_percent": 42.3,
          "crap": { "value": 28.42, "risk_level": "high" }
        },
        "threshold": 15.0,
        "exceeds": true
      }
    ]
  }
}

The envelope shape is locked across patch releases of the adapter that emits it — downstream consumers can pin against a schema_version.

GitHub Actions inline annotations

::warning file=src/lib.rs,line=42,title=CRAP 28.4::Function `process_request` has CRAP 28.42 (complexity=11, coverage=42.3%) which exceeds threshold 15.0

Renders as an inline annotation on the PR Files Changed tab — no GitHub Advanced Security / Code Scanning license required.

Markdown — PR-comment ready

# crap4rs v0.5.0 — CRAP Score Analysis

**Result:** PASS · **Functions:** 988 · **Above threshold (15):** 0

| Metric     | Worst | Average | Median |
|------------|------:|--------:|-------:|
| CRAP       | 13.00 |    1.62 |   1.00 |
| Complexity |    13 |     1.6 |    1.0 |
| Coverage   |  0.0% |   98.3% | 100.0% |

**Risk distribution:** low 951 · acceptable 22 · moderate 15 · high 0

HTML report

Interactive, sortable HTML report with per-function contributor drill-down. Coming in a future release — open an issue if you want early-access interest tracked.

Other formats

csv, sarif (Code Scanning surface), scorecard (single-row CI gate), scorecard-row (cross-adapter parity-locked CI row).

Multiple formats compose in one pass: --format json:envelope.json,markdown:report.md writes both from a single analysis.

Stability

crap-core is at 0.x and follows pre-1.0 semver — breaking changes can land on minor bumps; adapter crates pin against a specific minor. The wire envelope schema is locked once published — patch releases never change envelope shape; minor releases may add fields under #[serde(default)].

See also

License

Dual-licensed under MIT OR Apache-2.0 at your option.