Examples/Demos/Loan-Approval/Import-LoanPolicy.ps1

# Read the instructions and named choices from a small Markdown policy document.
function Import-LoanPolicy {
    param([string] $Path = "$PSScriptRoot/LoanPolicy.md")

    $text = Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
    $sections = [regex]::Match($text.Trim(), '(?ms)\A# Instructions[ \t]*\r?\n(?<Instructions>.*?)^# Choices[ \t]*\r?\n(?<Choices>.*)\z')
    if (-not $sections.Success) {
        throw 'Policy must contain # Instructions followed by # Choices.'
    }

    $instructions = $sections.Groups['Instructions'].Value.Trim()
    $choiceText = $sections.Groups['Choices'].Value.Trim()
    if (-not $instructions -or $choiceText -notmatch '\A##[ \t]+') {
        throw 'Policy needs instructions and choices with ## headings.'
    }
    if ($instructions -match '(?m)^# ' -or $choiceText -match '(?m)^# ') {
        throw 'Use only the two top-level sections: # Instructions and # Choices.'
    }

    $criteria = [ordered]@{}
    $choices = [regex]::Matches($choiceText, '(?ms)^##[ \t]+(?<Name>[^\r\n]+)(?:\r?\n|\z)(?<Description>.*?)(?=^##[ \t]+|\z)')
    foreach ($choice in $choices) {
        $name = $choice.Groups['Name'].Value.Trim()
        $description = $choice.Groups['Description'].Value.Trim()
        if (-not $name -or -not $description) {
            throw 'Every choice needs a name and a description.'
        }
        if ($criteria.Contains($name)) {
            throw "Duplicate policy choice: '$name'."
        }
        $criteria[$name] = $description
    }
    if ($criteria.Count -eq 0) {
        throw 'Policy needs at least one described choice.'
    }

    [pscustomobject]@{
        Instructions = $instructions
        Criteria     = $criteria
    }
}