# core/parsers/layer.parser.ts

> 72 lines of code and 18 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-parsers-layer-parser-ts
Source text: https://banes-lab.com/source/docs/core/parsers/layer.parser.ts.txt

Listed in [core/parsers](https://banes-lab.com/api/source/docs/core/parsers.md), after [core/parsers/export.parser.ts](https://banes-lab.com/source/docs/core/parsers/export.parser.ts.md) and before [core/parsers/location.parser.ts](https://banes-lab.com/source/docs/core/parsers/location.parser.ts.md).

## Definitions

- `splitConcernLayers` (lexical_declaration, line 69, exported)
- `layerOf` (lexical_declaration, line 78, exported)
- `markerId` (lexical_declaration, line 8)
- `inner` (lexical_declaration, line 12)
- `LayerParser` (class_declaration, line 16)
- `layers` (public_field_definition, line 17)
- `defects` (public_field_definition, line 18)
- `seen` (public_field_definition, line 19)
- `openId` (public_field_definition, line 20)
- `bodyLines` (public_field_definition, line 21)
- `feed` (method_definition, line 23)
- `trimmed` (lexical_declaration, line 24)
- `opened` (lexical_declaration, line 25)
- `closed` (lexical_declaration, line 30)
- `finish` (method_definition, line 40)
- `open` (method_definition, line 46)
- `close` (method_definition, line 58)
- `parser` (lexical_declaration, line 70, exported)

## Contained in

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

## Linked from

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

## Source

```typescript
import {
    LAYER_CLOSE_PREFIX as CLOSE_PREFIX,
    LAYER_MARKER_END as MARKER_END,
    LAYER_OPEN_PREFIX as OPEN_PREFIX,
} from "#configuration/constants/layer.constants";
import type { ConcernLayer, LayerDefect, LayerSplit } from "#types/markdown.types";

const markerId = function markerId(trimmed: string, prefix: string): string | null {
    if (!trimmed.startsWith(prefix) || !trimmed.endsWith(MARKER_END)) {
        return null;
    }
    const inner = trimmed.slice(prefix.length, trimmed.length - MARKER_END.length).trim();
    return inner.length > 0 ? inner : null;
};

class LayerParser {
    public readonly layers: ConcernLayer[] = [];
    public readonly defects: LayerDefect[] = [];
    private readonly seen = new Set<string>();
    private openId: string | null = null;
    private bodyLines: string[] = [];

    public feed(raw: string): void {
        const trimmed = raw.trim();
        const opened = markerId(trimmed, OPEN_PREFIX);
        if (opened !== null) {
            this.open(opened);
            return;
        }
        const closed = markerId(trimmed, CLOSE_PREFIX);
        if (closed !== null) {
            this.close(closed);
            return;
        }
        if (this.openId !== null) {
            this.bodyLines.push(raw);
        }
    }

    public finish(): void {
        if (this.openId !== null) {
            this.defects.push({ code: "unclosed-marker", id: this.openId });
        }
    }

    private open(id: string): void {
        if (this.openId !== null) {
            this.defects.push({ code: "unclosed-marker", id: this.openId });
        }
        if (this.seen.has(id)) {
            this.defects.push({ code: "duplicate-marker", id });
        }
        this.seen.add(id);
        this.openId = id;
        this.bodyLines = [];
    }

    private close(id: string): void {
        if (this.openId !== id) {
            this.defects.push({ code: "orphan-close", id });
            return;
        }
        this.layers.push({ body: this.bodyLines.join("\n"), id });
        this.openId = null;
        this.bodyLines = [];
    }
}

export const splitConcernLayers = function splitConcernLayers(doc: string): LayerSplit {
    const parser = new LayerParser();
    for (const raw of doc.split("\n")) {
        parser.feed(raw);
    }
    parser.finish();
    return { defects: parser.defects, layers: parser.layers };
};

export const layerOf = function layerOf(doc: string, id: string): string | null {
    return splitConcernLayers(doc).layers.find((layer) => layer.id === id)?.body ?? null;
};
```
