# core/producers/tool.scalastyle.producer.ts

> 73 lines of code and 24 definitions.

Tree: GovLab Quality
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-producers-tool-scalastyle-producer-ts
Source text: https://banes-lab.com/source/quality/core/producers/tool.scalastyle.producer.ts.txt

Listed in [core/producers](https://banes-lab.com/api/source/quality/core/producers.md), after [core/producers/tool.ruff.producer.ts](https://banes-lab.com/source/quality/core/producers/tool.ruff.producer.ts.md) and before [core/producers/tool.sqlfluff.producer.ts](https://banes-lab.com/source/quality/core/producers/tool.sqlfluff.producer.ts.md).

## Definitions

- `source` (lexical_declaration, line 62)
- `checkers` (lexical_declaration, line 25)
- `knobs` (lexical_declaration, line 71)
- `TOOL` (lexical_declaration, line 9)
- `VERSION` (lexical_declaration, line 10)
- `SOURCE` (lexical_declaration, line 11)
- `DOCS` (lexical_declaration, line 13)
- `CHECKER` (lexical_declaration, line 14)
- `CHECKER_CLOSE` (lexical_declaration, line 15)
- `SEGMENT_WINDOW` (lexical_declaration, line 16)
- `attr` (lexical_declaration, line 18)
- `key` (lexical_declaration, line 19)
- `end` (lexical_declaration, line 21)
- `found` (lexical_declaration, line 26)
- `at` (lexical_declaration, line 27)
- `tagEnd` (lexical_declaration, line 29)
- `close` (lexical_declaration, line 30)
- `ruleOf` (lexical_declaration, line 40)
- `className` (lexical_declaration, line 42)
- `level` (lexical_declaration, line 43)
- `produce` (lexical_declaration, line 66)
- `rules` (lexical_declaration, line 67)
- `id` (lexical_declaration, line 73)
- `fallback` (lexical_declaration, line 74)

## Contained in

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

## Uses

- [core/adapters/remote.adapter.ts](https://banes-lab.com/source/quality/core/adapters/remote.adapter.ts.md)
- [core/parsers/knob.parser.ts](https://banes-lab.com/source/quality/core/parsers/knob.parser.ts.md)

## Linked from

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

## Source

```typescript
import type { CatalogProduct, CatalogRule, KnobEntry } from "#types/catalog.types";
import { INTEGER_TYPE } from "#configuration/constants/knob.constants";
import { byRuleId } from "#core/comparators/rule.comparator";
import { defineProducer } from "#core/registries/producer.registry";
import { distinctRules } from "#core/selectors/catalog.selector";
import { isDigitsOnly } from "#core/parsers/knob.parser";
import { remoteText } from "#core/adapters/remote.adapter";

const TOOL = "scalastyle";
const VERSION = "master";
const SOURCE =
    "https://raw.githubusercontent.com/scalastyle/scalastyle/master/src/main/resources/scalastyle_definition.xml";
const DOCS = "https://www.scalastyle.org/rules-dev.html";
const CHECKER = "<checker";
const CHECKER_CLOSE = "</checker>";
const SEGMENT_WINDOW = 600;

const attr = function attr(text: string, name: string): string | null {
    const key = `${name}="`;
    const at = text.indexOf(key);
    const end = at === -1 ? -1 : text.indexOf('"', at + key.length);
    return end === -1 ? null : text.slice(at + key.length, end);
};

const checkers = function checkers(text: string): { segment: string; tag: string }[] {
    const found: { segment: string; tag: string }[] = [];
    let at = text.indexOf(CHECKER);
    while (at !== -1) {
        const tagEnd = text.indexOf(">", at);
        const close = text.indexOf(CHECKER_CLOSE, at);
        found.push({
            segment: text.slice(at, close === -1 ? at + SEGMENT_WINDOW : close),
            tag: text.slice(at, tagEnd === -1 ? text.length : tagEnd),
        });
        at = text.indexOf(CHECKER, at + CHECKER.length);
    }
    return found;
};

const ruleOf = function ruleOf(tag: string): CatalogRule[] {
    const id = attr(tag, "id");
    const className = attr(tag, "class");
    const level = attr(tag, "defaultLevel");
    return id === null
        ? []
        : [
              {
                  canonical: null,
                  category: "checker",
                  defaultLevel: level === "" ? null : level,
                  description: null,
                  ecosystem: "scala",
                  name: className === null || className === "" ? id : className.slice(className.lastIndexOf(".") + 1),
                  ruleId: id,
                  tool: TOOL,
                  toolVersion: VERSION,
                  url: DOCS,
              },
          ];
};

const source = async function source(): Promise<string> {
    return (await remoteText(SOURCE)) ?? "";
};

const produce = async function produce(): Promise<CatalogProduct[]> {
    const rules = distinctRules(checkers(await source()).flatMap(({ tag }) => ruleOf(tag))).toSorted(byRuleId);
    return [{ rules, source: TOOL, summary: { tool: TOOL, toolVersion: VERSION, total: rules.length } }];
};

const knobs = async function knobs(): Promise<KnobEntry[]> {
    return checkers(await source()).flatMap(({ segment, tag }): KnobEntry[] => {
        const id = attr(tag, "id");
        const fallback = (attr(segment, "default") ?? "").trim();
        return id !== null && isDigitsOnly(fallback)
            ? [[TOOL, id, { default: Number(fallback), knob: "value", threshold: true, type: INTEGER_TYPE }]]
            : [];
    });
};

defineProducer({ knobs, name: TOOL, produce, refresh: "manual" });
```
