# core/converters/inventory.converter.ts

> 143 lines of code and 35 definitions.

Tree: Build tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build#file-build-core-converters-inventory-converter-ts
Source text: https://banes-lab.com/assets/sources/source.fb152c9c60262a9689291f7b29d7be85367dbc4cd8732063013966395a8c33a9.generated.txt

## Definitions

- `slugPrefixAccepted` (lexical_declaration, line 33)
- `slugOf` (lexical_declaration, line 45)
- `NOT_FOUND` (lexical_declaration, line 20)
- `headingText` (lexical_declaration, line 22)
- `SlugSpan` (interface_declaration, line 40)
- `open` (lexical_declaration, line 46)
- `close` (lexical_declaration, line 50)
- `LockedSpan` (interface_declaration, line 57)
- `lockedOf` (lexical_declaration, line 62)
- `GateSpan` (interface_declaration, line 72)
- `gateOf` (lexical_declaration, line 77)
- `ruleOf` (lexical_declaration, line 85)
- `span` (lexical_declaration, line 86)
- `{ locked, rest }` (lexical_declaration, line 90)
- `{ directive, gate }` (lexical_declaration, line 94)
- `arrowOutsideParens` (lexical_declaration, line 98)
- `depth` (lexical_declaration, line 99)
- `at` (lexical_declaration, line 100)
- `char` (lexical_declaration, line 101)
- `stripLead` (lexical_declaration, line 111)
- `trimmed` (lexical_declaration, line 112)
- `avoidanceOf` (lexical_declaration, line 116)
- `colon` (lexical_declaration, line 120)
- `slug` (lexical_declaration, line 124)
- `directive` (lexical_declaration, line 125)
- `arrow` (lexical_declaration, line 126)
- `never` (lexical_declaration, line 130)
- `always` (lexical_declaration, line 131)
- `scanRules` (lexical_declaration, line 138, exported)
- `records` (lexical_declaration, line 139, exported)
- `tier` (lexical_declaration, line 140, exported)
- `line` (lexical_declaration, line 142, exported)
- `heading` (lexical_declaration, line 143, exported)
- `record` (lexical_declaration, line 148, exported)
- `lineCount` (lexical_declaration, line 156, exported)

## Source

```typescript
import {
    ALWAYS_WORD,
    ARROW,
    BACKTICK,
    BOLD_MARK,
    COLON,
    GATE_TAIL,
    HEADING_MARK,
    LEAD_IN_SEPARATOR,
    LINE_END,
    LIST_MARKER,
    LOCKED_MARKERS,
    NEVER_WORD,
    PAREN_CLOSE,
    PAREN_OPEN,
    RULE_PREFIX,
} from "#configuration/constants/inventory.constants";
import type { InventoryRecord } from "#types/inventory.types";

const NOT_FOUND = -1;

const headingText = function headingText(line: string): string | null {
    if (!line.startsWith(HEADING_MARK)) {
        return null;
    }
    let at = 0;
    while (at < line.length && line.charAt(at) === HEADING_MARK) {
        at += 1;
    }
    return line.slice(at).trim();
};

const slugPrefixAccepted = function slugPrefixAccepted(prefix: string): boolean {
    if (prefix.length === 0 || prefix === LIST_MARKER) {
        return true;
    }
    return prefix.startsWith(BOLD_MARK) && prefix.endsWith(BOLD_MARK + LEAD_IN_SEPARATOR);
};

interface SlugSpan {
    readonly slug: string;
    readonly tail: string;
}

const slugOf = function slugOf(line: string): SlugSpan | null {
    const open = line.indexOf(BACKTICK);
    if (open === NOT_FOUND || !slugPrefixAccepted(line.slice(0, open))) {
        return null;
    }
    const close = line.indexOf(BACKTICK, open + 1);
    if (close === NOT_FOUND || close === open + 1) {
        return null;
    }
    return { slug: line.slice(open + 1, close), tail: line.slice(close + 1) };
};

interface LockedSpan {
    readonly locked: boolean;
    readonly rest: string;
}

const lockedOf = function lockedOf(tail: string): LockedSpan {
    const trimmed = tail.trimStart();
    for (const marker of LOCKED_MARKERS) {
        if (trimmed.startsWith(marker)) {
            return { locked: true, rest: trimmed.slice(marker.length).trimStart() };
        }
    }
    return { locked: false, rest: trimmed };
};

interface GateSpan {
    readonly directive: string;
    readonly gate: string | null;
}

const gateOf = function gateOf(directive: string): GateSpan {
    const at = directive.indexOf(GATE_TAIL);
    if (at === NOT_FOUND) {
        return { directive: directive.trim(), gate: null };
    }
    return { directive: directive.slice(0, at).trim(), gate: directive.slice(at + GATE_TAIL.length).trim() };
};

const ruleOf = function ruleOf(line: string, source: string, tier: string): InventoryRecord | null {
    const span = slugOf(line);
    if (span === null) {
        return null;
    }
    const { locked, rest } = lockedOf(span.tail);
    if (!rest.startsWith(COLON)) {
        return null;
    }
    const { directive, gate } = gateOf(rest.slice(COLON.length));
    return { always: null, directive, gate, kind: "rule", locked, never: null, slug: span.slug, source, tier };
};

const arrowOutsideParens = function arrowOutsideParens(text: string): number {
    let depth = 0;
    for (let at = 0; at < text.length; at += 1) {
        const char = text.charAt(at);
        if (char === ARROW && depth === 0) {
            return at;
        }
        depth += char === PAREN_OPEN ? 1 : 0;
        depth -= char === PAREN_CLOSE ? 1 : 0;
    }
    return NOT_FOUND;
};

const stripLead = function stripLead(text: string, lead: string): string | null {
    const trimmed = text.trim();
    return trimmed.startsWith(lead) ? trimmed.slice(lead.length).trim() : null;
};

const avoidanceOf = function avoidanceOf(line: string, source: string, tier: string): InventoryRecord | null {
    if (!line.startsWith(RULE_PREFIX)) {
        return null;
    }
    const colon = line.indexOf(COLON);
    if (colon === NOT_FOUND) {
        return null;
    }
    const slug = line.slice(RULE_PREFIX.length, colon).trim();
    const directive = line.slice(colon + COLON.length).trim();
    const arrow = arrowOutsideParens(directive);
    if (arrow === NOT_FOUND) {
        return null;
    }
    const never = stripLead(directive.slice(0, arrow), NEVER_WORD);
    const always = stripLead(directive.slice(arrow + ARROW.length), ALWAYS_WORD);
    if (never === null || always === null) {
        return null;
    }
    return { always, directive, gate: null, kind: "avoidance", locked: false, never, slug, source, tier };
};

export const scanRules = function scanRules(text: string, source: string): InventoryRecord[] {
    const records: InventoryRecord[] = [];
    let tier = "";
    for (const raw of text.split(LINE_END)) {
        const line = raw.trim();
        const heading = headingText(line);
        if (heading !== null) {
            tier = heading;
            continue;
        }
        const record = ruleOf(line, source, tier) ?? avoidanceOf(line, source, tier);
        if (record !== null) {
            records.push(record);
        }
    }
    return records;
};

export const lineCount = function lineCount(text: string): number {
    return text.split(LINE_END).length;
};
```
