# core/validators/lexicon.validator.ts

> 115 lines of code and 20 definitions.

Tree: GovLab Context
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/context#file-context-core-validators-lexicon-validator-ts
Source text: https://banes-lab.com/source/context/core/validators/lexicon.validator.ts.txt

Listed in [core/validators](https://banes-lab.com/api/source/context/core/validators.md), after [core/validators/kind.validator.ts](https://banes-lab.com/source/context/core/validators/kind.validator.ts.md) and before [core/validators/node.validator.ts](https://banes-lab.com/source/context/core/validators/node.validator.ts.md).

## Definitions

- `defect` (lexical_declaration, line 22)
- `placedDefects` (lexical_declaration, line 37)
- `renameDefects` (lexical_declaration, line 65)
- `undeclaredDefects` (lexical_declaration, line 92)
- `tagExampleDefectsOf` (lexical_declaration, line 113, exported)
- `carriesStray` (lexical_declaration, line 82)
- `expectedFolder` (lexical_declaration, line 26)
- `placed` (lexical_declaration, line 41)
- `coversOf` (lexical_declaration, line 56)
- `covering` (lexical_declaration, line 58)
- `before` (lexical_declaration, line 69)
- `after` (lexical_declaration, line 70)
- `covered` (lexical_declaration, line 74)
- `folderByTagOf` (lexical_declaration, line 103)
- `tag` (lexical_declaration, line 106)
- `folder` (lexical_declaration, line 107)
- `terms` (lexical_declaration, line 114, exported)
- `placedTerms` (lexical_declaration, line 115, exported)
- `placedTags` (lexical_declaration, line 116, exported)
- `folderByTag` (lexical_declaration, line 117, exported)

## Contained in

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

## Uses

- [configuration/strings/lexicon.strings.ts](https://banes-lab.com/source/context/configuration/strings/lexicon.strings.ts.md)
- [core/selectors/lexicon.selector.ts](https://banes-lab.com/source/context/core/selectors/lexicon.selector.ts.md)

## Used by

- [core/validators/ontology.validator.ts](https://banes-lab.com/source/context/core/validators/ontology.validator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/context/folder-context-configuration-strings.md)
- [core/selectors](https://banes-lab.com/anatomy/context/folder-context-core-selectors.md)
- [core/validators](https://banes-lab.com/anatomy/context/folder-context-core-validators.md)

## Source

```typescript
import type { Lexicon, PlacedFile, Term } from "#types/lexicon.types";
import {
    MALFORMED_RENAME,
    NO_EXAMPLE,
    NO_RENAME,
    STRAY_EXAMPLE,
    SUBJECT_CHANGED,
    UNNAMED_TAG,
    exampleFolderMismatch,
    exampleTagMismatch,
    misplacedExample,
    renameFromDeclaredTag,
    renameUncovered,
    undeclaredPlacement,
    undeclaredRefusal,
} from "#configuration/strings/lexicon.strings";
import { folderOfTerm, isMirrored, statesPlacement, statesRefusal, tagOfTerm } from "#core/selectors/lexicon.selector";
import { EXAMPLE_SHAPES } from "#configuration/constants/lexicon.constants";
import type { LexDefect } from "#types/validation.types";
import { placedFileOf } from "#core/matchers/filename.matcher";

const defect = function defect(term: Term, reason: string): LexDefect {
    return { id: term.id, reason };
};

const expectedFolder = function expectedFolder(
    term: Term,
    placed: PlacedFile,
    folderByTag: ReadonlyMap<string, string>,
): string | null {
    if (!isMirrored(term)) {
        return folderOfTerm(term);
    }
    return placed.variant === null ? null : (folderByTag.get(placed.variant) ?? null);
};

const placedDefects = function placedDefects(term: Term, folderByTag: ReadonlyMap<string, string>): LexDefect[] {
    if (term.example === undefined) {
        return [defect(term, NO_EXAMPLE)];
    }
    const placed = placedFileOf(term.example);
    if (placed === null) {
        return [defect(term, misplacedExample(term.example))];
    }
    const tag = tagOfTerm(term);
    const folder = expectedFolder(term, placed, folderByTag);
    if (tag === null || folder === null) {
        return [defect(term, UNNAMED_TAG)];
    }
    return [
        ...(placed.tag === tag ? [] : [defect(term, exampleTagMismatch(placed.tag, tag))]),
        ...(placed.folder === folder ? [] : [defect(term, exampleFolderMismatch(placed.folder, folder))]),
    ];
};

const coversOf = function coversOf(term: Term, lex: Lexicon): { tag: string; folder: string }[] {
    return term.seeAlso.flatMap((ref) => {
        const covering = lex.resolve(ref);
        const tag = covering === null ? null : tagOfTerm(covering);
        const folder = covering === null ? null : folderOfTerm(covering);
        return tag === null || folder === null ? [] : [{ folder, tag }];
    });
};

const renameDefects = function renameDefects(term: Term, lex: Lexicon, placedTags: ReadonlySet<string>): LexDefect[] {
    if (term.exemplar === undefined) {
        return [defect(term, NO_RENAME)];
    }
    const before = placedFileOf(term.exemplar.before);
    const after = placedFileOf(term.exemplar.after);
    if (before === null || after === null) {
        return [defect(term, MALFORMED_RENAME)];
    }
    const covered = coversOf(term, lex).some((cover) => cover.tag === after.tag && cover.folder === after.folder);
    return [
        ...(before.subject === after.subject ? [] : [defect(term, SUBJECT_CHANGED)]),
        ...(placedTags.has(before.tag) ? [defect(term, renameFromDeclaredTag(before.tag))] : []),
        ...(covered ? [] : [defect(term, renameUncovered(after.folder, after.tag))]),
    ];
};

const carriesStray = function carriesStray(term: Term): boolean {
    if (term.exampleShape === EXAMPLE_SHAPES.placed) {
        return term.exemplar !== undefined;
    }
    if (term.exampleShape === EXAMPLE_SHAPES.renamed) {
        return term.example !== undefined;
    }
    return term.example !== undefined || term.exemplar !== undefined;
};

const undeclaredDefects = function undeclaredDefects(term: Term): LexDefect[] {
    return [
        ...(statesPlacement(term) && term.exampleShape !== EXAMPLE_SHAPES.placed
            ? [defect(term, undeclaredPlacement(EXAMPLE_SHAPES.placed))]
            : []),
        ...(statesRefusal(term) && term.exampleShape !== EXAMPLE_SHAPES.renamed
            ? [defect(term, undeclaredRefusal(EXAMPLE_SHAPES.renamed))]
            : []),
    ];
};

const folderByTagOf = function folderByTagOf(placedTerms: readonly Term[]): ReadonlyMap<string, string> {
    return new Map(
        placedTerms.flatMap((term): [string, string][] => {
            const tag = tagOfTerm(term);
            const folder = folderOfTerm(term);
            return tag === null || folder === null ? [] : [[tag, folder]];
        }),
    );
};

export const tagExampleDefectsOf = function tagExampleDefectsOf(lex: Lexicon): LexDefect[] {
    const terms = lex.all();
    const placedTerms = terms.filter((term) => term.exampleShape === EXAMPLE_SHAPES.placed);
    const placedTags = new Set(placedTerms.flatMap((term) => tagOfTerm(term) ?? []));
    const folderByTag = folderByTagOf(placedTerms);
    return terms.flatMap((term) => [
        ...undeclaredDefects(term),
        ...(carriesStray(term) ? [defect(term, STRAY_EXAMPLE)] : []),
        ...(term.exampleShape === EXAMPLE_SHAPES.placed ? placedDefects(term, folderByTag) : []),
        ...(term.exampleShape === EXAMPLE_SHAPES.renamed ? renameDefects(term, lex, placedTags) : []),
    ]);
};
```
