ProjectTools.psm1

$Script:Toolbelt = (Split-Path $PROFILE) + "\ProjectTools.json"
$Script:ProjectsJSON = (Split-Path $PROFILE) + "\Projects.json"

$Script:Tools = @()
if ($Script:Toolbelt) {
    $Script:Tools += Get-Content $Script:Toolbelt | ConvertFrom-Json
}

$Script:Projects = @()
if ($Script:ProjectsJSON) {
    $Script:Projects += Get-Content $Script:ProjectsJSON | ConvertFrom-Json
}

<#
.SYNOPSIS
   Installs the POSH ProjectTools
.DESCRIPTION
   Creates the .json files required by the ProjectTools module,
   scans the current environment and adds compatible tools to it's toolbelt
   and registers with the powershell environment to start when PowerShell is launched.
   Only needs to be run once, not everytime you open a PowerShell prompt.
 
.PARAMETER Rescan
    Scans the environment for new tools and creates a new tools database from scratch.
 
.PARAMETER Force
    OverWrites any existing files and creates new ones.
    In essence this means that the existing Tools and Project databases are completly whiped.
 
.EXAMPLE
    PS C:\> Install-ProjectTools -Rescan
 
    Will generate a new ProjectTools.JSON file
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function Install-ProjectToolsEnvironment {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
    Param(
        [switch]$Rescan,
        [switch]$Force
    )
    process {
        Write-Verbose "Checking for existing PowerShell profiles."
        if (!(Test-Path $PROFILE)) {
            if ($PSCmdlet.ShouldProcess("Creation of PowerShell Profile $PROFILE Successful")) {
                Write-Verbose "Creating a PowerShell profile."
                New-Item -Path $PROFILE -ItemType File -Force | Out-Null
            }
        }
        elseif (Get-Content $PROFILE | ForEach-Object {$_ -notmatch "Initialize-ProjectToolsEnvironment"}) {
            # Kazaamjt: Fucks up in a really weird way without "-Encoding ascii" on my machine
            Write-Output "`nInitialize-ProjectToolsEnvironment" | Out-File $PROFILE -Append -Encoding ascii
            Write-Verbose "Creating ProjectTools.json file."
            New-Item -Path $Script:Toolbelt -ItemType File -Force | Out-Null
        }

        Write-Verbose "Checking environment to add items to toolbelt."
        $SupportedTools = @()

        if (!($Script:Toolbelt) -or $Rescan) {
            # check git
            Write-Verbose "Checking git"
            $Git = Get-Command git.exe 2>&1
            if ($Git.Name) {
                $Object = New-Object -TypeName psobject
                $Object | Add-Member -MemberType NoteProperty -Name "Name" -Value "git"
                $Object | Add-Member -MemberType NoteProperty -Name "Command" -Value $Git.Name
                $Object | Add-Member -MemberType NoteProperty -Name "Path" -Value $Git.Path
                $SupportedTools += $Object
                Write-Verbose "git added."
            } else {
                Write-Verbose "git not Added."
            }

            # Check VSCode
            Write-Verbose "Checking VSCode."
            $VSCode = Get-Command code.cmd 2>&1
            if ($VSCode.Name) {
                $Object = New-Object -TypeName psobject
                $Object | Add-Member -MemberType NoteProperty -Name "Name" -Value "VSCode"
                $Object | Add-Member -MemberType NoteProperty -Name "Command" -Value $VSCode.Name
                $Object | Add-Member -MemberType NoteProperty -Name "Path" -Value $VSCode.Path
                $SupportedTools += $Object
                Write-Verbose "VSCode added."
            } else {
                Write-Verbose "VSCode not Added."
            }

            # Check Python
            $Python = Get-Command python.exe 2>&1
            if ($Python.Name) {
                $Object = New-Object -TypeName psobject
                $Object | Add-Member -MemberType NoteProperty -Name "Name" -Value "Python"
                $Object | Add-Member -MemberType NoteProperty -Name "Command" -Value $Python.Name
                $Object | Add-Member -MemberType NoteProperty -Name "Path" -Value $Python.Path
                $SupportedTools += $Object
                Write-Verbose "Python Added."

                # Check venv
                Write-Verbose "Checking virtualEnv."
                if (($Python.Version.Major -eq 3) -and ($Python.Version.Minor -ge 3)) {
                    $Builtin = $true
                }
                elseif (($Python.Version.Major -eq 2) -and ($Python.Version.Minor -ge 7)) {
                    if ((python.exe --version).replace("Python 2.7.", "") -ge 9 ) {
                        $Builtin = $true
                    } else {
                        Write-Verbose "VirtualEnv not added."
                    }
                } else {
                    Write-Verbose "VirtualEnv not added."
                }

                if ($Builtin) {
                    Write-Verbose "VirtualEnv added."
                    $Object = New-Object -TypeName psobject
                    $Object | Add-Member -MemberType NoteProperty -Name "Name" -Value "VirtualEnv"
                    $Object | Add-Member -MemberType NoteProperty -Name "Command" -Value "python.exe -m venv"
                    $SupportedTools += $Object
                }

            } else {
                Write-Verbose "Python not Added."
            }

            # Add the tools to the belt
            Write-Verbose "Commiting found tools to ProjectTools.json"
            $SupportedTools | ConvertTo-Json | Out-File $Script:Toolbelt
        }

        Write-Verbose "Checking for existing Projects.json file."
        if ($Force -or !(Test-Path $Script:ProjectsJSON)) {
            Write-Verbose "Creating Projects.json file."
            New-Item -Path $Script:ProjectsJSON -ItemType File -Force | Out-Null
        }
    }
}

<#
.SYNOPSIS
    Starts the ProjectTools environment
 
.DESCRIPTION
    Loads the last used Project.
    This cmdlet is not meant for use outside PowerShell Profiles.
    Should only be executed upon opening a new PowerShell session.
#>

function Initialize-ProjectToolsEnvironment {
    if ($env:CurrentProject) {
        $Project = GetProject -Name $env:CurrentProject

        if ($Project) {
            LoadProject -Project $Project
        }
    }
}

<#
.SYNOPSIS
    Creates a new Project and registers it in the ProjectTools database.
 
.DESCRIPTION
    Creates a new Project with the following steps:
    - If the name of the project is the same as the current path, it uses this path,
      otherwise it uses the name of the project to create a folder or the path parameter.
    - Based on the available tools it sets up an environment.
      e.g.: It creates a local git repo, a readme.md, virtual env, etc..
      It does not overwrite any existing objects in the folder.
    - Finaly it adds the project to the project database.
 
.PARAMETER Name
    Name of the project. If a path is not set a directory is created with the same name.
    If the current path has the same name as the name parameter it uses this instead.
    See the examples for more info.
 
.PARAMETER Path
    A path for the new project.
    Can be a relative or a direct path.
 
.PARAMETER ProjectType
    The type of project this will be.
 
.PARAMETER ToolsDir
    Creates a directory under the path in which tools can be placed.
    Can be a path, but must be relative to the set path of the project.
 
.PARAMETER PSTools
    Name of the tools module.
    This module will be loaded and unloaded automatically upon switching projects.
    Can be a path, but must be relative to the set path of the ToolsDir.
 
.PARAMETER NoVirtualEnv
    If a python project is selected and virtualEnv is available, but you don't want a virtual env.
    VirtualEnv can take a while to set up, so this is for you don't want to wait that long.
 
.PARAMETER VirtualEnvDir
    Name of the virtualEnv directory that is being created.
    If the project is not a python project, VirtualEnv is not available or NoVirtualEnv is selected, this is ignore.
    If a directory with the same name already existed it is used as the VirtualEnv directory instead.
    Can be a path, but must be relative to the set path of the project.
 
.EXAMPLE
    PS C:\Users\John\Projects> New-Project test
 
    Creates a directory called test under "C:\Users\John\Projects" using the blank project template.
 
.EXAMPLE
    PS C:\Users\John\Projects\test> New-Project test
 
    Creates a project called test, using the already existing test directory currently selected.
    The Project path will thus be: "C:\Users\John\Projects\test"
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function New-Project {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
    param(
        [parameter(mandatory=$True, Position=0)]
        [string]$Name,
        [string]$Path,
        [validateset('PowerShellScript', 'PowerShellModule', 'C', 'C++', 'Python', 'blank')]
        [string]$ProjectType='blank',
        [string]$ToolsDir='tools',
        [string]$PSTools='Tools.psm1',
        [switch]$NoVirtualEnv,
        [string]$VirtualEnvDir='venv'
    )
    process {
        try {
            GetProject -Name $Name
            throw "Project already exists."
        } catch {
            $Project = New-Object -TypeName psobject
            $Project | Add-Member -MemberType NoteProperty -Name "Name" -Value $Name

            # TODO: This is some gross, hard-to-follow nesting. Need to clean this up.
            if (!($Path)) {
                if (Test-Path "..\$Name") {
                    if ((get-location).Path -eq (Resolve-Path "..\$Name").Path) {
                        $Path = "..\$Name"
                    } else {
                        $Path = ".\$Name"
                    }
                } else {
                    $Path = ".\$Name"
                }
            }
            if (!(Test-Path $Path)) {
                New-Item $Path -ItemType Directory -Force | Out-Null
            }

            if ($pscmdlet.ShouldProcess("$Path", "Resolving and adding to project")) {
                $Project | Add-Member -MemberType NoteProperty -Name "Path" -Value (Resolve-Path $Path).Path
                Push-Location $Path
            }

            if ($Script:Tools | Where-Object {$_.Name -eq 'git'}) {
                if (!(Test-Path ".\.git")) {
                    git.exe init .
                    Write-Output "# $Name`n" | Out-File "readme.md"
                    if (!(Test-Path ".gitignore")) {
                        New-Item -Path ".gitignore" -ItemType File | Out-Null
                    }
                    git.exe add .  | Out-Null
                    git.exe commit -m "initial commit" | Out-Null
                }
            }

            $Project | Add-Member -MemberType NoteProperty -Name 'ProjectType' -Value $ProjectType
            switch ($ProjectType) {
                'PowerShellScript' {
                    if (!(Test-Path "$Name.ps1")) {
                        New-Item "$Name.ps1" -ItemType File  | Out-Null
                    }
                    $Project | Add-Member -MemberType NoteProperty -Name "PrimaryFile" -Value "$Name.ps1"
                }
                'PowerShellModule' {
                    if (!(Test-Path "$Name.psm1")) {
                        New-Item "$Name.psm1" -ItemType File  | Out-Null
                    }
                    if (!(Test-Path "$Name.psd1")) {
                        New-ModuleManifest -Path "$Name.psd1" -Author $env:USERNAME | Out-Null
                    }
                    $Project | Add-Member -MemberType NoteProperty -Name "PrimaryFile" -Value "$Name.psm1"
                }
                'C' {
                    $CreatePSTools = $True
                    if (!(Test-Path "$Name.c")) {
                        New-Item "$Name.c" -ItemType File | Out-Null
                    }
                    $BoilerPlate = "function Build-$Name {`n `n}`n"
                    $Project | Add-Member -MemberType NoteProperty -Name "PrimaryFile" -Value "$Name.c"
                }
                'C++' {
                    $CreatePSTools = $True
                    if (!(Test-Path "$Name.cpp")) {
                        New-Item "$Name.cpp" -ItemType File | Out-Null
                    }
                    $BoilerPlate = "function Build-$Name {`n `n}`n"
                    $Project | Add-Member -MemberType NoteProperty -Name "PrimaryFile" -Value "$Name.cpp"
                }
                'Python' {
                    $CreatePSTools = $True
                    if (($Script:Tools | Where-Object {$_.Name -eq 'VirtualEnv'}) -and ($NoVirtualEnv -eq $false)) {
                        if (!(Test-Path $VirtualEnvDir)) {
                            Write-Verbose "Creating python VirtualEnv."
                            $venv = ($Script:Tools | Where-Object {$_.Name -eq 'VirtualEnv'}).Command + " $VirtualEnvDir"
                            & $venv
                        }
                        $Project | Add-Member -MemberType NoteProperty -Name 'VirtualEnv' -Value $VirtualEnvDir

                        Write-Output "$VirtualEnvDir/" | Out-File ".gitignore" -Append
                    } else {
                        $Project | Add-Member -MemberType NoteProperty -Name 'VirtualEnv' -Value $null
                    }
                    if (!(Test-Path "$Name.py")) {
                        New-Item "$Name.py" -ItemType File  | Out-Null
                    }
                    Write-Output "__pycache__/" | Out-File ".gitignore" -Append
                    $BoilerPlate = "function Start-$Name {`n python $Name.py`n}`n"
                    $Project | Add-Member -MemberType NoteProperty -Name "PrimaryFile" -Value "$Name.py"
                }

                default {
                    $Project | Add-Member -MemberType NoteProperty -Name "PrimaryFile" -Value $null
                }
            }

            if ($CreatePSTools) {
                if (!(Test-Path "$ToolsDir")) {
                    New-Item -Path "$ToolsDir" -ItemType Directory  | Out-Null
                }
                if (!(Test-Path "$ToolsDir\$PSTools")) {
                    New-Item -Path "$ToolsDir\$PSTools" -ItemType File  | Out-Null
                    $BoilerPlate | Out-File -FilePath "$ToolsDir\$PSTools"
                }
                $Project | Add-Member -MemberType NoteProperty -Name 'ToolsDir' -Value "$ToolsDir"
                $Project | Add-Member -MemberType NoteProperty -Name 'PSTools' -Value "$PSTools"
            }

            if ($pscmdlet.ShouldProcess("ProjectToolsDatabase", "Add project")) {
                $Script:Projects += $Project
                $Script:Projects | ConvertTo-Json | Out-File $Script:ProjectsJSON
                Pop-Location
            }
            if ($pscmdlet.ShouldProcess("$Name", "Switch-Project")) {
                Switch-Project -Name $Name
            }
        }
    }
}

<#
.SYNOPSIS
    Gets a project or several projects from the project database.
 
.DESCRIPTION
    Gets a single or list of projects from the project database.
    Can be used with wildcards. (Wildcard is *)
    See examples.
 
.PARAMETER Name
    The name of the project you want.
    Accepts wildcards.
 
.EXAMPLE
    PS C:\Users\John\Projects> Get-Project *Test*
 
    Gets any and all projects containing "Test".
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function Get-Project {
    [CmdletBinding()]
    param(
        [string]$Name='*'
    )
    process {
        $Projects = $Script:Projects | Where-Object {$_.Name -like $Name}
        return $Projects
    }
}

<#
.SYNOPSIS
    Selects an existing project and updates it's settings.
 
.DESCRIPTION
    Selects an existing project and updates it's settings.
    Unlike New-Project it does not create any new files or environments,
    it merely updates what the projects object is pointing to.
 
.PARAMETER Name
    Name of the project.
 
.PARAMETER Path
    A path for the new project.
    Can be a relative or a direct path.
 
.PARAMETER ProjectType
    The type of project this will be.
 
.PARAMETER PrimaryFile
    The name of the primary execution file.
    Can be a path, but must be relative to the set path of the project.
 
.PARAMETER ToolsDir
    Name of the ToolDirectory.
    Will not be created if set this way.
    Can be a path, but must be relative to the set path of the project.
 
.PARAMETER PSTools
    Name of the PSTools module file.
    Will not be created if set this way.
    Can be a path, but must be relative to the set path of the ToolsDir.
 
.PARAMETER VirtualEnvDir
    Sets the name of the virtualEnvDir.
    Can be a path, but must be relative to the set path of the project.
 
.PARAMETER RemoveSettings
    Removes the specified setting from the chooses project.
    Overwrites the specified setting if it was set in the same command.
 
.EXAMPLE
    PS C:\Users\John\Projects> Set-Project SomeProject -VirtualEnvDir ".\VirtualEnvironmentDir"
 
    Sets the virtual environment directory path to "Path\to\project\VirtualEnvironmentDir".
    This will cause the project tools to try to load VirtualEnvironmentDir as the VirtuelEnv directory.
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function Set-Project {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
    param(
        [parameter(mandatory=$True, Position=0)]
        [string]$Name,
        [string]$Path,
        [validateset('PowerShellScript', 'PowerShellModule', 'C', 'C++', 'Python', 'blank')]
        [string]$ProjectType,
        [string]$PrimaryFile,
        [string]$ToolsDir,
        [string]$PSTools,
        [string]$VirtualEnvDir,
        [validateset('ToolsDir','PSTools','VirtualEnvDir')]
        [string]$RemoveSettings
    )
    process {
        $Project = GetProject -Name $Name

        if ($Path) {
            if ($pscmdlet.ShouldProcess("$Name", "Update path")){
                $Project.Path = (Resolve-Path $Path).Path
            }
        }

        if ($ProjectType) {
            $Project.ProjectType = $ProjectType
        }

        if ($PrimaryFile) {
            $Project.PrimaryFile = $PrimaryFile
        }

        if ($ToolsDir) {
            $Project.ToolsDir = $ToolsDir
        }

        if ($PSTools) {
            $Project.PSTools = $PSTools
        }

        if ($VirtualEnvDir) {
            $Project.VirtualEnv = $VirtualEnvDir
        }

        switch ($RemoveSettings) {
            'ToolsDir' { $Project.ToolsDir = $null; break}
            'PSTools' {$Project.PSTools = $null; break}
            'VirtualEnvDir' {$Project.VirtualEnv = $null; break}
        }

        $NewProjects = @()
        $OldProjects = Get-Content $Script:ProjectsJSON | ConvertFrom-Json
        foreach ($OldProject in $OldProjects) {
            if ($OldProject.Name -ne $Name) {
                $NewProjects += $OldProject
            }
        }

        if ($pscmdlet.ShouldProcess("ProjectToolsDatabase", "Updating project $Name")) {
            $Script:Projects = $NewProjects
            $Script:Projects += $Project
            $Script:Projects | ConvertTo-Json | Out-File $Script:ProjectsJSON
        }
    }
}

<#
.SYNOPSIS
     Unloads the current project and loads a new project.
 
.DESCRIPTION
    Checks if the given project exists, then,
    checks if there are any projects currently loaded and unloads them.
    Finaly it loads the given projects environment.
 
.PARAMETER Name
    Name of the project.
 
.EXAMPLE
    PS C:\Users\John\Projects> Switch-Project some-other-project
 
    Unloads the current environment and loads some-other-project if it exists
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function Switch-Project {
    [CmdletBinding()]
    param(
        [parameter(mandatory=$True, Position=0)]
        [string]$Name
    )
    process {
        # Set environment Variables
        $Project = GetProject -Name $Name

        # If there was a previous project, unload it.
        if ($env:CurrentProject) {
            $CurrentProject = Get-Project $env:CurrentProject
            UnloadProject -Project $CurrentProject
        }

        LoadProject -Project $Project
    }
}

<#
.SYNOPSIS
    Unloads the current project environment and loads it again.
 
.DESCRIPTION
    This cmdlet is meant for when you make changes to the environment and need it to be reloaded.
    For example, changes to the tools.psm1 module are not loaded automatically.
    Using this cmdlet will load any changes made.
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function Reset-ProjectEnvironment {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='None')] param()
    Switch-Project -Name $env:CurrentProject
}

<#
.SYNOPSIS
    Removes the given project from the project database.
 
.DESCRIPTION
    Used to delete a project from the project database.
    Optionally deletes the whole project directory.
    Be carefull deleting anything using this command as it will whipe everything in it's root directory.
 
.PARAMETER Name
    Name of the project.
 
.PARAMETER RemoveFiles
    Remove the root project directory and anything in it.
    It is not advised to use this.
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function Remove-Project {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
    param(
        [parameter(mandatory=$True, Position=0)]
        [string]$Name,
        [switch]$RemoveFiles
    )
    process {
        $Project = GetProject -Name $Name
        if ($Project) {
            if ($pscmdlet.ShouldProcess("ProjectToolsDatabase", "removing project $Name")) {
                $Script:Projects = $Script:Projects | Where-Object {$_.Name -ne $Name}
                $Script:Projects | ConvertTo-Json | Out-File $Script:ProjectsJSON
            }

            if ((Get-Location).Path -eq (Resolve-Path $Project.Path).Path) {
                if ($pscmdlet.ShouldProcess(($Project.Path + "\.."), "Set-Location")) {
                    Set-Location ($Project.Path + '\..')
                }
            }

            if ($Project.Name -eq $env:CurrentProject) {
                if ($pscmdlet.ShouldProcess("$Name", "Unload Project")) {
                    UnloadProject -Project $Project
                }

                if ($pscmdlet.ShouldProcess("Env:", "Updating environment")) {
                    Remove-Item Env:\CurrentProject -ErrorAction SilentlyContinue
                    [Environment]::SetEnvironmentVariable("CurrentProject",$null,"User")
                }
            }

            if ($RemoveFiles) {
                Remove-Item -Path $Project.Path -Recurse -Force -Confirm
            }
        } else {
            throw "No such project."
        }
    }
}

<#
.SYNOPSIS
    Uninstalls the POSH-ProjectTools.
 
.DESCRIPTION
    Removes all traces of the POSH-ProjectTools from the environment.
    Does not remove the ProjectTools install-directory, this must be deleted manualy.
    Wipes the project database, but does not delete any projects or environments.
 
.LINK
    https://github.com/kazaamjt/POSH-ProjectTools
#>

function Remove-ProjectToolsEnvironment {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')]
    param(
        [switch]$KeepDatabaseFiles
    )
    process {
        Write-Warning -Message "This will clean your environment, but the module itself will still be under: $PSScriptRoot"
        if ($pscmdlet.ShouldProcess("Env:", "Updating environment")) {
            Remove-Item Env:\CurrentProject -ErrorAction SilentlyContinue
            [Environment]::SetEnvironmentVariable("CurrentProject",$null,"User")
        }

        if (!($KeepDatabaseFiles)) {
            Remove-Item $Script:Toolbelt -ErrorAction SilentlyContinue
            Remove-Item $Script:ProjectsJSON -ErrorAction SilentlyContinue
        }

        if ($pscmdlet.ShouldProcess($PROFILE, "Updating userProfile")) {
            $ProfileContent = Get-Content $PROFILE
            if (Get-Content $PROFILE | ForEach-Object {$_ -match "Initialize-ProjectToolsEnvironment"}) {
                $ProfileContent.Replace("Initialize-ProjectToolsEnvironment", "") | Out-File $PROFILE
            }
        }
    }
}


# Internal functions
# These functions are only meant to be used internally.
# Because of this, they are not user facing.
function SetCurrentEnvVar {
    param([string]$Name)
    # Change for current session
    $env:CurrentProject = $Name
    # Change for subsequent sessions
    [System.Environment]::SetEnvironmentVariable('CurrentProject', $env:CurrentProject, [System.EnvironmentVariableTarget]::User)
}

# A strict version of get-project. No wildcards here.
function GetProject{
    param([string]$Name)
    process {
        $Project = $Script:Projects | Where-Object {$_.Name -eq $Name}
        if ($Project) {
            return $Project
        } else {
            $ProjectNotFoundError ="Project $Name was not found."
            throw $ProjectNotFoundError
        }
    }
}

function LoadProject {
    param($Project)
    SetCurrentEnvVar -Name $Project.Name
        Set-Location -Path $Project.Path
        if ($Project.VirtualEnv) {
            & ($Project.VirtualEnv +"\Scripts\Activate.ps1")
        }

        if ($Project.PSTools) {
            Import-Module (Resolve-Path ($Project.Path + "\" +$Project.ToolsDir + "\" + $Project.PSTools)).Path -WarningAction SilentlyContinue -Global
        }
}

function UnloadProject {
    param($Project)
    process {
        try {
            deactivate
        }
        catch [system.management.automation.commandnotfoundexception] {
            Write-Verbose "Deactivate was not found, this is ok if venv is not active."
        }

        if ($Project.PSTools) {
            $PSToolsModule = Get-Module -Name ($Project.PSTools).replace(".psm1", "")
        }
        if ($PSToolsModule) {
            Remove-Module -Name ($Project.PSTools).replace(".psm1", "")
        }
    }
}