NerdFonts.psm1

[CmdletBinding()]
param()
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath)
$script:PSModuleInfo = Test-ModuleManifest -Path "$PSScriptRoot\$baseName.psd1"
$script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ }
$scriptName = $script:PSModuleInfo.Name
Write-Debug "[$scriptName] - Importing module"
#region [classes] - [private]
Write-Debug "[$scriptName] - [classes] - [private] - Processing folder"
#region [classes] - [private] - [Scope]
Write-Debug "[$scriptName] - [classes] - [private] - [Scope] - Importing"
enum Scope {
    CurrentUser
    AllUsers
}
Write-Debug "[$scriptName] - [classes] - [private] - [Scope] - Done"
#endregion [classes] - [private] - [Scope]
Write-Debug "[$scriptName] - [classes] - [private] - Done"
#endregion [classes] - [private]
#region [functions] - [public]
Write-Debug "[$scriptName] - [functions] - [public] - Processing folder"
#region [functions] - [public] - [completers]
Write-Debug "[$scriptName] - [functions] - [public] - [completers] - Importing"
Register-ArgumentCompleter -CommandName Install-NerdFont, Get-NerdFont -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
    $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter

    Get-NerdFont | Select-Object -ExpandProperty Name | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
        [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
    }
}
Write-Debug "[$scriptName] - [functions] - [public] - [completers] - Done"
#endregion [functions] - [public] - [completers]
#region [functions] - [public] - [Get-NerdFont]
Write-Debug "[$scriptName] - [functions] - [public] - [Get-NerdFont] - Importing"
function Get-NerdFont {
    <#
        .SYNOPSIS
        Get NerdFonts asset list

        .DESCRIPTION
        Get NerdFonts asset list, filtered by name, from the latest release.

        .EXAMPLE
        Get-NerdFonts -Name 'FiraCode'

        .EXAMPLE
        Get-NerdFonts -Name '*Code'
    #>

    [Alias('Get-NerdFonts')]
    [CmdletBinding()]
    param (
        # Name of the NerdFont to get
        [Parameter()]
        [SupportsWildcards()]
        [string] $Name = '*'
    )

    Write-Verbose "Selecting assets by name: '$Name'"
    $script:NerdFonts | Where-Object { $_.Name -like $Name }
}
Write-Debug "[$scriptName] - [functions] - [public] - [Get-NerdFont] - Done"
#endregion [functions] - [public] - [Get-NerdFont]
#region [functions] - [public] - [Install-NerdFont]
Write-Debug "[$scriptName] - [functions] - [public] - [Install-NerdFont] - Importing"
#Requires -Modules Admin, Fonts

function Install-NerdFont {
    <#
        .SYNOPSIS
        Installs Nerd Fonts to the system.

        .DESCRIPTION
        Installs Nerd Fonts to the system.

        .EXAMPLE
        Install-NerdFont -Name 'Fira Code'

        Installs the font 'Fira Code' to the current user.

        .EXAMPLE
        Install-NerdFont -Name 'Fira Code' -Scope AllUsers

        Installs the font 'Fira Code' to all users. This requires to be run as administrator.

        .EXAMPLE
        Install-NerdFont -All

        Installs all Nerd Fonts to the current user.
    #>

    [CmdletBinding(
        DefaultParameterSetName = 'ByName',
        SupportsShouldProcess
    )]
    [Alias('Install-NerdFonts')]
    param(
        # Specify the name of the Nerd font(s) to install.
        [Parameter(
            ParameterSetName = 'ByName',
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string[]] $Name,

        # Specify to install all Nerd Font(s).
        [Parameter(
            ParameterSetName = 'All',
            Mandatory
        )]
        [switch] $All,

        # Specify the scope of where to install the font(s).
        [Parameter()]
        [Scope] $Scope = 'CurrentUser'
    )

    begin {
        if ($Scope -eq 'AllUsers' -and -not (IsAdmin)) {
            $errorMessage = @'
Administrator rights are required to install fonts.
Please run the command again with elevated rights (Run as Administrator) or provide '-Scope CurrentUser' to your command."
'@

            throw $errorMessage
        }
        $nerdFontsToInstall = @()

        $tempPath = Join-Path -Path $HOME -ChildPath '.temp'
        if (-not (Test-Path -Path $tempPath -PathType Container)) {
            Write-Verbose "Create folder [$tempPath]"
            $null = New-Item -Path $tempPath -ItemType Directory
            $tempFolderCreated = $true
        }

        $Name = $PSBoundParameters.Name
    }

    process {
        if ($All) {
            $nerdFontsToInstall = $script:NerdFonts
        } else {
            foreach ($fontName in $Name) {
                $nerdFontsToInstall += $script:NerdFonts | Where-Object Name -EQ $fontName
            }
        }

        Write-Verbose "[$Scope] - Installing [$($nerdFontsToInstall.count)] fonts"

        foreach ($NerdFont in $nerdFontsToInstall) {
            $URL = $NerdFont.URL
            $fontName = $NerdFont.Name
            $downloadPath = Join-Path -Path $tempPath -ChildPath "$FontName$script:ArchiveExtension"
            $extractPath = Join-Path -Path $tempPath -ChildPath "$fontName"

            Write-Verbose "[$fontName] - Downloading to [$downloadPath]"
            $storedProgressPreference = $ProgressPreference
            $ProgressPreference = 'SilentlyContinue' # Suppress progress bar
            if ($PSCmdlet.ShouldProcess($fontName, "Download $fontName")) {
                Invoke-WebRequest -Uri $URL -OutFile $downloadPath -Verbose:$false
            }
            $ProgressPreference = $storedProgressPreference

            Write-Verbose "[$fontName] - Unpack to [$extractPath]"
            if ($PSCmdlet.ShouldProcess($fontName, 'Extract archive')) {
                Expand-Archive -Path $downloadPath -DestinationPath $extractPath -Force
                Remove-Item -Path $downloadPath -Force
            }

            Write-Verbose "[$fontName] - Install to [$Scope]"
            if ($PSCmdlet.ShouldProcess($fontName, 'Install font')) {
                Install-Font -Path $extractPath -Scope $Scope
                Remove-Item -Path $extractPath -Force -Recurse
            }
        }
    }

    end {
        if ($tempFolderCreated) {
            Write-Verbose "Remove folder [$tempPath]"
            Remove-Item -Path $tempPath -Force
        }
    }
}
Write-Debug "[$scriptName] - [functions] - [public] - [Install-NerdFont] - Done"
#endregion [functions] - [public] - [Install-NerdFont]
Write-Debug "[$scriptName] - [functions] - [public] - Done"
#endregion [functions] - [public]
#region [variables] - [private]
Write-Debug "[$scriptName] - [variables] - [private] - Processing folder"
#region [variables] - [private] - [NerdFonts]
Write-Debug "[$scriptName] - [variables] - [private] - [NerdFonts] - Importing"
$script:Release = Invoke-RestMethod 'https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest'
$script:NerdFonts = $script:Release.assets.browser_download_url | Where-Object { $_ -like '*.zip' } | ForEach-Object {
    [pscustomobject]@{
        Name    = $_.Split('/')[-1].Split('.')[0]
        Version = $script:Release.tag_name
        URL     = $_
    }
}
$script:ArchiveExtension = '.zip'
Write-Debug "[$scriptName] - [variables] - [private] - [NerdFonts] - Done"
#endregion [variables] - [private] - [NerdFonts]
Write-Debug "[$scriptName] - [variables] - [private] - Done"
#endregion [variables] - [private]

#region Member exporter
$exports = @{
    Alias    = '*'
    Cmdlet   = ''
    Function = @(
        'Get-NerdFont'
        'Install-NerdFont'
    )
}
Export-ModuleMember @exports
#endregion Member exporter