# tools/core/runners/role.runner.ts

> 55 lines of code and 11 definitions.

Tree: Coordination tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-runners-role-runner-ts
Source text: https://banes-lab.com/assets/sources/source.1e55d9f8c9ee410d6de083e6926c853a60ab80464f77e339889a90e9dea655e6.generated.txt

## Definitions

- `filled` (lexical_declaration, line 22)
- `runRole` (lexical_declaration, line 39, exported)
- `PLACEHOLDER_OPEN` (lexical_declaration, line 6)
- `PLACEHOLDER_CLOSE` (lexical_declaration, line 8)
- `RoleRequest` (interface_declaration, line 10, exported)
- `RoleOutcome` (interface_declaration, line 17, exported)
- `colon` (lexical_declaration, line 23)
- `key` (lexical_declaration, line 24)
- `template` (lexical_declaration, line 48, exported)
- `sections` (lexical_declaration, line 49, exported)
- `lines` (lexical_declaration, line 55, exported)

## Uses

- [tools/core/inspectors/role.inspector.ts](https://banes-lab.com/source/coordination/tools/core/inspectors/role.inspector.ts.md)
- [tools/core/strings/role.strings.ts](https://banes-lab.com/source/coordination/tools/core/strings/role.strings.ts.md)

## Source

```typescript
import { CONCERN_MISSING, ROLE_EXISTS, TEMPLATE_WITHOUT_SECTIONS, roleRaised } from "../strings/role.strings.ts";

import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { roleFieldsFrom, roleSectionsFrom, roleSubsectionsFrom } from "../inspectors/role.inspector.ts";

const PLACEHOLDER_OPEN = "<";

const PLACEHOLDER_CLOSE = ">";

export interface RoleRequest {
    readonly absolute: string;
    readonly template: string;
    readonly letter: string;
    readonly concern: string;
}

export interface RoleOutcome {
    readonly code: number;
    readonly message: string;
}

const filled = function filled(field: string, letter: string, concern: string): string {
    const colon = field.indexOf(":");
    const key = colon === -1 ? field : field.slice(0, colon);

    if (key === "letter") {
        return `letter: ${letter}`;
    }
    if (key === "concern") {
        return `concern: ${concern}`;
    }
    if (key === "name") {
        return `name: ${concern}.${letter.toLowerCase()}`;
    }

    return field.endsWith(":") ? `${field} ${PLACEHOLDER_OPEN}unwritten${PLACEHOLDER_CLOSE}` : field;
};

export const runRole = function runRole(request: RoleRequest): RoleOutcome {
    if (request.concern.trim().length === 0) {
        return { code: 2, message: CONCERN_MISSING };
    }

    if (existsSync(request.absolute)) {
        return { code: 2, message: ROLE_EXISTS };
    }

    const template = readFileSync(request.template, "utf8");
    const sections = roleSectionsFrom(template);

    if (sections.length === 0) {
        return { code: 2, message: TEMPLATE_WITHOUT_SECTIONS };
    }

    const lines: string[] = ["---"];
    for (const field of roleFieldsFrom(template)) {
        lines.push(filled(field, request.letter, request.concern));
    }
    lines.push("---", "");

    for (const section of sections) {
        lines.push(section, "", `${PLACEHOLDER_OPEN}unwritten${PLACEHOLDER_CLOSE}`, "");
    }

    for (const subsection of roleSubsectionsFrom(template)) {
        lines.push(subsection, "", `${PLACEHOLDER_OPEN}unwritten${PLACEHOLDER_CLOSE}`, "");
    }

    writeFileSync(request.absolute, lines.join("\n"), "utf8");

    return { code: 0, message: roleRaised(request.letter, request.concern) };
};
```
