Documentarian.Private.psm1

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

#region Functions.Private

function Get-YamlHeader {

    [CmdletBinding()]
    param([string]$Path)

    $doc = Get-Content $path -Encoding UTF8
    $hasFrontmatter = Select-String -Pattern '^---$' -Path $path
    $start = 0
    $end = $doc.count

    if ($hasFrontmatter) {
        $start = $hasFrontmatter[0].LineNumber
        $end = $hasFrontmatter[1].LineNumber - 2
    }
    $doc[$start..$end]

}

function GetMDLinks {
    param(
        $mdlinks, # regex matches for [label](target)
        $reflinks # regex matches for [label][ref]
    )

    foreach ($mdlink in $mdlinks.Matches) {
        # Skip INCLUDE and tab links
        if (-not $mdlink.Value.Trim().StartsWith('[!INCLUDE') -and
            -not $mdlink.Value.Trim().Contains('#tab/')
        ) {
            $linkitem = [pscustomobject]([ordered]@{
                    mdlink = ''
                    target = ''
                    ref    = ''
                    label  = ''
                })
            switch ($mdlink.Groups) {
                { $_.Name -eq 'link' } { $linkitem.mdlink = $_.Value }
                { $_.Name -eq 'target' } { $linkitem.target = $_.Value }
                { $_.Name -eq 'label' } { $linkitem.label = $_.Value }
            }
            $linkitem
        }
    }

    foreach ($reflink in $reflinks.Matches) {
        $linkitem = [pscustomobject]([ordered]@{
                mdlink = ''
                target = ''
                ref    = ''
                label  = ''
            })
        switch ($reflink.Groups) {
            { $_.Name -eq 'link' } { $linkitem.mdlink = $_.Value }
            { $_.Name -eq 'label' } { $linkitem.label = $_.Value }
            { $_.Name -eq 'ref' } { $linkitem.ref = $_.Value }
        }
        $linkitem
    }
}

function GetRefTargets {
    param(
        $refdefs # results from regex match
    )
    $RefTargets = @{}
    foreach ($refdef in $refdefs.Matches) {
        $refitem = [pscustomobject]([ordered]@{
                refdef = ''
                target = ''
                ref    = ''
            })

        switch ($refdef.Groups) {
            { $_.Name -eq 'refdef' } { $refitem.refdef = $_.Value }
            { $_.Name -eq 'target' } { $refitem.target = $_.Value }
            { $_.Name -eq 'ref' } { $refitem.ref = $_.Value }
        }
        if (!$RefTargets.ContainsKey($refitem.ref)) {
            $RefTargets.Add(
                $refitem.ref,
                [pscustomobject]@{
                    target = $refitem.target
                    ref    = $refitem.ref
                    refdef = $refitem.refdef
                }
            )
        }
    }
    $RefTargets
}

function hash2yaml {

    [CmdletBinding()]
    param([hashtable]$MetaHash)

    ### This is a naive implementation of a YAML serializer. It is not intended to be a complete
    ### implementation. It converts all members of the hashtable to single-line strings, and does
    ### not support any of the more complex YAML features. It is intended to be used to serialize
    ### the metadata hashtable that is passed to the Markdown template.

    ForEach-Object {
        '---'
        ForEach ($key in ($MetaHash.keys | Sort-Object)) {
            if ('' -ne $MetaHash.$key) {
                '{0}: {1}' -f $key, $MetaHash.$key
            }
        }
        '---'
    }

}

function IsInCodeBlock {
    param(
        $linenumber,
        $codefences
    )
    foreach ($fence in $codefences) {
        if ($linenumber -ge $fence.Start -and $linenumber -le $fence.End) {
            return $true
        }
    }
    return $false

}

function New-ParsedDocument {
  [CmdletBinding()]
  [OutputType('ParsedDocument')]
  param(
    [Parameter(Mandatory)]
    [System.IO.FileInfo]$FileInfo,

    [Parameter(Mandatory)]
    [AllowEmptyString()]
    [string]$RawContent,

    [Parameter(Mandatory)]
    [AllowNull()]
    [Markdig.Syntax.MarkdownDocument]$ParsedMarkdown,

    [Parameter()]
    [System.Collections.Specialized.OrderedDictionary]$FrontMatter,

    [Parameter(Mandatory)]
    [AllowEmptyString()]
    [string]$Body
  )

  process {
    $Document = [ParsedDocument]::new()

    $Document.FileInfo = $FileInfo
    $Document.RawContent = $RawContent
    $Document.ParsedMarkdown = $ParsedMarkdown
    if ($FrontMatter) {
      $Document.FrontMatter = $FrontMatter
    }
    $Document.Body = $Body

    $Document.ParseLinksFromBody()

    $Document
  }
}

#endregion Functions.Private