PSProjectStatus.psm1


#dot source functions
Get-ChildItem $psscriptroot\functions\*.ps1 -Recurse |
ForEach-Object {
    . $_.FullName
}

#region class definitions
enum PSProjectStatus {
    Development
    Updating
    Stable
    AlphaTesting
    BetaTesting
    ReleaseCandidate
    Patching
    UnitTesting
    AcceptanceTesting
    Other
}

Enum gitMode {
    fetch
    push
}

Class PSProjectRemote {
    [string]$Name
    [string]$Url
    [gitMode]$Mode

    PSProjectRemote ($Name, $url, $mode) {
        $this.Name = $Name
        $this.url = $url
        $this.mode = $mode
    }
}

Class PSProject {
    [string]$Name = (Split-Path (Get-Location).path -Leaf)
    [string]$Path = (Convert-Path (Get-Location).path)
    [datetime]$LastUpdate = (Get-Date)
    [string[]]$Tasks = @()
    [PSProjectStatus]$Status = "Development"
    [Version]$ProjectVersion = (Test-ModuleManifest ".\$(Split-Path $pwd -Leaf).psd1" -ErrorAction SilentlyContinue).version
    [string]$GitBranch = ""
    #using .NET classes to ensure compatibility with non-Windows platforms
    [string]$UpdateUser = "$([system.environment]::UserDomainName)\$([System.Environment]::Username)"
    [string]$Computername = [System.Environment]::MachineName
    [PSProjectRemote[]]$RemoteRepository = @()
    [string]$Comment = "none"

    [void]Save() {
        $json = Join-Path -Path $this.path -ChildPath psproject.json
        #convert the ProjectVersion to a string in the JSON file
        #convert the LastUpdate to a formatted date string
        $this | Select-Object @{Name = '$schema'; Expression = { "https://raw.githubusercontent.com/jdhitsolutions/PSProjectStatus/main/psproject.schema.json" } },
        Name, Path,
        @{Name="LastUpdate";Expression={ "{0:o}" -f $_.LastUpdate}},
        @{Name = "Status"; Expression = { $_.status.toString() } },
        @{Name = "ProjectVersion"; Expression = { $_.ProjectVersion.toString() } },
        UpdateUser, Computername, RemoteRepository, Tasks, GitBranch, Comment |
        ConvertTo-Json | Out-File -FilePath $json -Encoding utf8
    }
    [void]RefreshProjectVersion() {
        $this.ProjectVersion = (Test-ModuleManifest ".\$(Split-Path $pwd -Leaf).psd1" -ErrorAction SilentlyContinue).version
    }
    [void]RefreshUser() {
        $this.UpdateUser = "$([system.environment]::UserDomainName)\$([System.Environment]::Username)"
    }
    [void]RefreshComputer() {
        $this.Computername = [System.Environment]::MachineName
    }
    [void]RefreshRemoteRepository() {
        if (Test-Path .git) {
            $remotes = git remote -v
            if ($remotes) {
                $repos = @()
                foreach ($remote in $remotes) {
                    $split = $remote.split()
                    $RemoteName = $split[0]
                    $Url = $split[1]
                    $Mode = $split[2].replace("(", "").Replace(")", "")
                    $repos += [PSProjectRemote]::new($remotename, $url, $mode)
                } #foreach
                $this.RemoteRepository = $repos
            } #if remotes found
        }
    }

    [void]RefreshAll() {
        $this.RefreshProjectVersion()
        $this.RefreshUser()
        $this.RefreshComputer()
        $this.RefreshRemoteRepository()
        $this.Save()
    }
}

#endregion

#region add a VSCode/PowerShell ISE extension to the project

if ($host.name -eq 'visual studio code host') {
    Function Update-PSProjectStatus {
        [cmdletbinding()]
        Param ($context)

        $title = "$([char]27)[4;3;38;5;228mStatus Options$([char]27)[0m"

        $menu = @"
 
        $title
 
        [1] Development [6] ReleaseCandidate
        [2] Updating [7] Patching
        [3] Stable [8] UnitTesting
        [4] AlphaTesting [9] AcceptanceTesting
        [5] BetaTesting [10] Other
 
"@


        Do {
            Clear-Host
            Write-Host $menu
            [int]$r = Read-Host "Select a project status. Enter no value to cancel"
            if ($r -eq 0) {
                #cancel
                return
            }
            if ($r -lt 1 -OR $r -gt 10) {
                $pseditor.Window.ShowWarningMessage("You entered an invalid value. Enter nothing or a value between 1 and 10.")
            }

        } until ($r -ge 1 -AND $r -le 10)

        $pseditor.Window.SetStatusBarMessage("Updating PSProject status", 3000)
        switch ($r) {
            1 { $status = "Development" }
            2 { $status = "Updating" }
            3 { $status = "Stable" }
            4 { $status = "AlphaTesting" }
            5 { $status = "BetaTesting" }
            6 { $status = "ReleaseCandidate" }
            7 { $status = "Patching" }
            8 { $status = "UnitTesting" }
            9 { $status = "AcceptanceTesting" }
            10 { $status = "Other" }
        }

        if ($status) {
            #update the project if a status is specified
            $splat = @{
                LastUpdate = (Get-Date)
                Status     = $status
            }
            if (Test-Path ".\$(Split-Path $pwd -Leaf).psd1" ) {
                $splat.Add("ProjectVersion", (Test-ModuleManifest ".\$(Split-Path $pwd -Leaf).psd1").version)
            }

            $s = Set-PSProjectStatus @splat | Select-Object VersionInfo | Out-String
            #parse out ANSI escape sequences
            $detail = $s -replace "$([char]27)\[[\d;]*m", ''
            #show a summary message
            $pseditor.Window.ShowInformationMessage($detail)
        }

    }#end function

    Register-EditorCommand -Function "Update-PSProjectStatus" -name "UpdatePSProjectStatus" -DisplayName "Update PSProject Status"
} #VSCode

if ($host.name -match "ISE") {
    #The ISE specific version of the update function
    Function Update-PSProjectStatus {
        [cmdletbinding()]
        Param ()

        $title = "Status Options`n --------------"

        $menu = @"
 
    $title
 
    [1] Development [6] ReleaseCandidate
    [2] Updating [7] Patching
    [3] Stable [8] UnitTesting
    [4] AlphaTesting [9] AcceptanceTesting
    [5] BetaTesting [10] Other
 
"@


        Write-Host $menu

        [int]$r = Read-Host "Select a project status. Enter no value to cancel"
        if ($r -lt 1 -OR $r -gt 10) {
            return "You entered an invalid value. "
        }
        switch ($r) {
            1 { $status = "Development" }
            2 { $status = "Updating" }
            3 { $status = "Stable" }
            4 { $status = "AlphaTesting" }
            5 { $status = "BetaTesting" }
            6 { $status = "ReleaseCandidate" }
            7 { $status = "Patching" }
            8 { $status = "UnitTesting" }
            9 { $status = "AcceptanceTesting" }
            10 { $status = "Other" }
        }

        if ($status) {
            #update the project if a status is specified
            $splat = @{
                LastUpdate = (Get-Date)
                Status     = $status
            }
            if (Test-Path ".\$(Split-Path $pwd -Leaf).psd1" ) {
                $splat.Add("ProjectVersion", (Test-ModuleManifest ".\$(Split-Path $pwd -Leaf).psd1").version)
            }

            Set-PSProjectStatus @splat | Select-Object -Property VersionInfo
        }

    }#end function
    if ($psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.DisplayName -notcontains "Update PSProjectStatus") {
        #add the action to the Add-Ons menu
        [void]($psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Update PSProjectStatus", { Update-PSProjectStatus }, $Null))
    }
} #ISE

#endregion

#path to the JSON schema file
$jsonSchema = "https://raw.githubusercontent.com/jdhitsolutions/PSProjectStatus/main/psproject.schema.json"

# for testing
# $jsonSchema = "file:///c:/scripts/psprojectstatus/psproject.schema.json"