NerdFonts.psm1

[CmdletBinding()]
param()
$scriptName = 'NerdFonts'
Write-Verbose "[$scriptName] - Importing module"

#region - From [init]
Write-Verbose "[$scriptName] - [init] - Processing folder"

#region - From [init] - [initializer]
Write-Verbose "[$scriptName] - [init] - [initializer] - Importing"

Write-Verbose '----------------------------------------'
Write-Verbose '--- Initializing the NerdFonts list ---'
Write-Verbose '----------------------------------------'
$script:Release = Invoke-RestMethod 'https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest'
$script:NerdFonts = $Release.assets.browser_download_url | Where-Object { $_ -like '*.zip' } | ForEach-Object {
    [pscustomobject]@{
        Name    = $_.Split('/')[-1].Split('.')[0]
        Version = $Release.tag_name
        URL     = $_
    }
}
$script:ArchiveExtension = '.zip'

Write-Verbose "[$scriptName] - [init] - [initializer] - Done"
#endregion - From [init] - [initializer]

Write-Verbose "[$scriptName] - [init] - Done"
#endregion - From [init]

#region - From [classes] - [private]
Write-Verbose "[$scriptName] - [classes] - [private] - Processing folder"

#region - From [classes] - [private] - [Scope]
Write-Verbose "[$scriptName] - [classes] - [private] - [Scope] - Importing"

enum Scope {
    CurrentUser
    AllUsers
}

Write-Verbose "[$scriptName] - [classes] - [private] - [Scope] - Done"
#endregion - From [classes] - [private] - [Scope]

Write-Verbose "[$scriptName] - [classes] - [private] - Done"
#endregion - From [classes] - [private]

#region - From [functions] - [public]
Write-Verbose "[$scriptName] - [functions] - [public] - Processing folder"

#region - From [functions] - [public] - [Get-NerdFont]
Write-Verbose "[$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-Verbose "[$scriptName] - [functions] - [public] - [Get-NerdFont] - Done"
#endregion - From [functions] - [public] - [Get-NerdFont]
#region - From [functions] - [public] - [Install-NerdFont]
Write-Verbose "[$scriptName] - [functions] - [public] - [Install-NerdFont] - Importing"

#Requires -Modules Admin, Fonts, DynamicParams

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 = 'Name',
        SupportsShouldProcess
    )]
    [Alias('Install-NerdFonts')]
    param(
        # 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'
    )

    dynamicparam {
        $DynamicParamDictionary = New-DynamicParamDictionary

        $dynPath = @{
            Name                            = 'Name'
            Type                            = [string[]]
            Mandatory                       = $true
            ParameterSetName                = 'Name'
            HelpMessage                     = 'Name of the font to install.'
            ValueFromPipeline               = $true
            ValueFromPipelineByPropertyName = $true
            ValidateSet                     = Get-NerdFonts | Select-Object -ExpandProperty Name
            DynamicParamDictionary          = $DynamicParamDictionary
        }
        New-DynamicParam @dynPath

        return $DynamicParamDictionary
    }

    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-Verbose "[$scriptName] - [functions] - [public] - [Install-NerdFont] - Done"
#endregion - From [functions] - [public] - [Install-NerdFont]

Write-Verbose "[$scriptName] - [functions] - [public] - Done"
#endregion - From [functions] - [public]


$exports = @{
    Alias    = '*'
    Cmdlet   = ''
    Function = @(
        'Get-NerdFont'
        'Install-NerdFont'
    )
}
Export-ModuleMember @exports