# core/adapters/tool.clippy.adapter.ts

> 62 lines of code and 17 definitions.

Tree: GovLab Quality
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-adapters-tool-clippy-adapter-ts
Source text: https://banes-lab.com/source/quality/core/adapters/tool.clippy.adapter.ts.txt

Listed in [core/adapters](https://banes-lab.com/api/source/quality/core/adapters.md), after [core/adapters/tool.clang-tidy.adapter.ts](https://banes-lab.com/source/quality/core/adapters/tool.clang-tidy.adapter.ts.md) and before [core/adapters/tool.clj-kondo.adapter.ts](https://banes-lab.com/source/quality/core/adapters/tool.clj-kondo.adapter.ts.md).

## Definitions

- `call` (lexical_declaration, line 49, exported)
- `clippyArgs` (lexical_declaration, line 23, exported)
- `runClippy` (lexical_declaration, line 41, exported)
- `clippySpec` (lexical_declaration, line 29)
- `TOOL` (lexical_declaration, line 17)
- `BIN` (lexical_declaration, line 18)
- `LANGUAGE` (lexical_declaration, line 19)
- `CONFIG_FILE` (lexical_declaration, line 20)
- `CAUSE` (lexical_declaration, line 21)
- `fixArgs` (lexical_declaration, line 24, exported)
- `lintArgs` (lexical_declaration, line 25, exported)
- `confFile` (lexical_declaration, line 42, exported)
- `lints` (lexical_declaration, line 47, exported)
- `advisory` (lexical_declaration, line 48, exported)
- `report` (lexical_declaration, line 55, exported)
- `fix` (lexical_declaration, line 56, exported)
- `fixed` (lexical_declaration, line 61, exported)

## Contained in

- [core/adapters](https://banes-lab.com/anatomy/quality/folder-quality-core-adapters.md)

## Uses

- [configuration/strings/tool.strings.ts](https://banes-lab.com/source/quality/configuration/strings/tool.strings.ts.md)
- [core/adapters/tool.adapter.ts](https://banes-lab.com/source/quality/core/adapters/tool.adapter.ts.md)
- [core/factories/finding.factory.ts](https://banes-lab.com/source/quality/core/factories/finding.factory.ts.md)
- [core/parsers/tool.clippy.parser.ts](https://banes-lab.com/source/quality/core/parsers/tool.clippy.parser.ts.md)
- [core/resolvers/scope.resolver.ts](https://banes-lab.com/source/quality/core/resolvers/scope.resolver.ts.md)
- [core/selectors/failure.selector.ts](https://banes-lab.com/source/quality/core/selectors/failure.selector.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/quality/folder-quality-configuration-strings.md)
- [core/adapters](https://banes-lab.com/anatomy/quality/folder-quality-core-adapters.md)
- [core/factories](https://banes-lab.com/anatomy/quality/folder-quality-core-factories.md)
- [core/parsers](https://banes-lab.com/anatomy/quality/folder-quality-core-parsers.md)
- [core/resolvers](https://banes-lab.com/anatomy/quality/folder-quality-core-resolvers.md)
- [core/selectors](https://banes-lab.com/anatomy/quality/folder-quality-core-selectors.md)

## Source

```typescript
import type { AdvisoryContext, RunnerContext, ScanSpec, ToolCall } from "#types/tool.types";
import { CARGO_MANIFEST, SUCCESS_STATUSES } from "#configuration/constants/tool.constants";
import { NO_STDERR_DETAIL, installHint, toolErrorBecause } from "#configuration/strings/tool.strings";
import { enabledToolRules, nativeToolConfig } from "#core/resolvers/tool.resolver";
import { failedOnStatus, failedWhenEmpty } from "#core/classifiers/invocation.classifier";
import { fixCycle, passTool, residualOf } from "#core/adapters/tool.adapter";
import type { RunResult } from "#types/finding.types";
import { defineTool } from "#core/registries/tool.registry";
import { gatingAdvisory } from "#core/factories/tool.factory";
import { parseClippyOutput } from "#core/parsers/tool.clippy.parser";
import path from "node:path";
import { projectDirWithMarker } from "#core/resolvers/scope.resolver";
import { tailLines } from "#core/selectors/failure.selector";
import { toolFailure } from "#core/factories/finding.factory";
import { writeToolFile } from "#core/persistence/tool.persistence";

const TOOL = "clippy";
const BIN = "cargo";
const LANGUAGE = "rust";
const CONFIG_FILE = "clippy.toml";
const CAUSE = "a malformed config or cargo error";

export const clippyArgs = function clippyArgs(lints: readonly string[], withFix: boolean): string[] {
    const fixArgs = withFix ? ["--fix", "--allow-dirty", "--allow-no-vcs"] : [];
    const lintArgs = lints.flatMap((lint) => ["-W", lint]);
    return ["clippy", ...fixArgs, "--message-format", "json", ...(lintArgs.length > 0 ? ["--", ...lintArgs] : [])];
};

const clippySpec = function clippySpec(advisory: AdvisoryContext, failed: ScanSpec["failed"]): ScanSpec {
    return {
        failed,
        failure: (exit) =>
            toolFailure(
                advisory,
                toolErrorBecause(`${BIN} ${TOOL}`, exit.status, CAUSE, tailLines(exit.stderr, NO_STDERR_DETAIL)),
            ),
        parse: (result) => parseClippyOutput(result.stdout, advisory.ecosystem),
    };
};

export const runClippy = async function runClippy(context: RunnerContext): Promise<RunResult> {
    const confFile = writeToolFile(
        context.root,
        CONFIG_FILE,
        await nativeToolConfig(context.root, LANGUAGE, CONFIG_FILE),
    );
    const lints = await enabledToolRules(context.root, LANGUAGE, TOOL);
    const advisory = gatingAdvisory(context, TOOL, installHint(TOOL));
    const call = (withFix: boolean): ToolCall => ({
        args: clippyArgs(lints, withFix),
        bin: BIN,
        cwd: projectDirWithMarker(context.root, context.paths, CARGO_MANIFEST),
        env: { ...context.env, CLIPPY_CONF_DIR: path.dirname(confFile) },
    });
    const report = clippySpec(advisory, failedWhenEmpty(SUCCESS_STATUSES));
    const fix = clippySpec(advisory, failedOnStatus(SUCCESS_STATUSES));
    return fixCycle(
        passTool(advisory, call(false), report),
        (findings) => context.fix && findings.length > 0,
        (reported) => {
            const fixed = passTool(advisory, call(true), fix);
            return fixed.terminal ? fixed.result : residualOf(reported, passTool(advisory, call(false), report));
        },
    );
};

defineTool({ ecosystems: [LANGUAGE], run: runClippy, tool: TOOL });
```
