# shared/matchers/segment.matcher.ts

> 58 lines of code and 18 definitions.

Tree: Governance tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-matchers-segment-matcher-ts
Source text: https://banes-lab.com/source/governance/shared/matchers/segment.matcher.ts.txt

Listed in [shared/matchers](https://banes-lab.com/api/source/governance/shared/matchers.md), after [shared/matchers/pattern.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/pattern.matcher.ts.md) and before [shared/matchers/vocabulary.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/vocabulary.matcher.ts.md).

## Definitions

- `isUpper` (lexical_declaration, line 12)
- `isLower` (lexical_declaration, line 8)
- `isDigit` (lexical_declaration, line 16)
- `humpedWords` (lexical_declaration, line 34)
- `isWellFormedJoiner` (lexical_declaration, line 57, exported)
- `isLowerOrDigit` (lexical_declaration, line 20)
- `fitsLetters` (lexical_declaration, line 24)
- `joinedWords` (lexical_declaration, line 29)
- `isLetter` (lexical_declaration, line 25)
- `slotWordOf` (lexical_declaration, line 67, exported)
- `wordsOf` (lexical_declaration, line 61, exported)
- `LOWER` (lexical_declaration, line 3)
- `UPPER` (lexical_declaration, line 4)
- `DIGITS` (lexical_declaration, line 5)
- `SLOT_JOINER` (lexical_declaration, line 6)
- `first` (lexical_declaration, line 35)
- `words` (lexical_declaration, line 39)
- `current` (lexical_declaration, line 40)

## Used by

- [shared/matchers/filename.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/filename.matcher.ts.md)
- [shared/validators/taxonomy.validator.ts](https://banes-lab.com/source/governance/shared/validators/taxonomy.validator.ts.md)

## Records this file grounds

- [Word-Boundary Case](https://banes-lab.com/records/lexicon/word-boundary-case.md)

## Source

```typescript
import type { LetterCase, Splitter } from "../../types/taxonomy.types.ts";

const LOWER = "abcdefghijklmnopqrstuvwxyz";
const UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const DIGITS = "0123456789";
const SLOT_JOINER = "-";

const isLower = function isLower(char: string): boolean {
    return char.length === 1 && LOWER.includes(char);
};

const isUpper = function isUpper(char: string): boolean {
    return char.length === 1 && UPPER.includes(char);
};

const isDigit = function isDigit(char: string): boolean {
    return char.length === 1 && DIGITS.includes(char);
};

const isLowerOrDigit = function isLowerOrDigit(char: string): boolean {
    return isLower(char) || isDigit(char);
};

const fitsLetters = function fitsLetters(word: string, letters: LetterCase): boolean {
    const isLetter = letters === "upper" ? isUpper : isLower;
    return word !== "" && Array.from(word, (char) => isLetter(char) || isDigit(char)).every(Boolean);
};

const joinedWords = function joinedWords(stem: string, joiner: string, letters: LetterCase): readonly string[] | null {
    const words = stem.split(joiner);
    return words.every((word) => fitsLetters(word, letters)) ? words.map((word) => word.toLowerCase()) : null;
};

const humpedWords = function humpedWords(stem: string, leadsUpper: boolean): readonly string[] | null {
    const first = stem.charAt(0);
    if (stem === "" || isUpper(first) !== leadsUpper || !(isUpper(first) || isLower(first))) {
        return null;
    }
    const words: string[] = [];
    let current = "";
    for (const char of stem) {
        if (isUpper(char)) {
            if (current !== "") {
                words.push(current);
            }
            current = char.toLowerCase();
        } else if (isLowerOrDigit(char)) {
            current += char;
        } else {
            return null;
        }
    }
    words.push(current);
    return words;
};

export const isWellFormedJoiner = function isWellFormedJoiner(joiner: string): boolean {
    return joiner !== "" && ![...joiner].some((char) => isLower(char) || isUpper(char) || isDigit(char));
};

export const wordsOf = function wordsOf(stem: string, splitter: Splitter): readonly string[] | null {
    return splitter.joiner === null
        ? humpedWords(stem, splitter.letters === "upper")
        : joinedWords(stem, splitter.joiner, splitter.letters);
};

export const slotWordOf = function slotWordOf(words: readonly string[]): string {
    return words.join(SLOT_JOINER);
};
```
