Private/Exercises.ps1
|
function Get-RePSPlatform { <# .SYNOPSIS Returns the current platform name as used in exercise metadata. #> [CmdletBinding()] [OutputType([string])] param() if ($IsWindows) { 'Windows' } elseif ($IsMacOS) { 'MacOS' } else { 'Linux' } } function Get-RePSExerciseData { <# .SYNOPSIS Loads every exercise.psd1 under Exercises/ (cached per session). #> [CmdletBinding()] param( [string]$Path = $script:ExercisesPath, [switch]$Refresh ) if ($script:ExerciseCache -and -not $Refresh -and $Path -eq $script:ExercisesPath) { return $script:ExerciseCache } $exercises = foreach ($manifest in Get-ChildItem -Path $Path -Filter 'exercise.psd1' -Recurse -File) { $data = Import-PowerShellDataFile -LiteralPath $manifest.FullName $folder = $manifest.Directory.FullName [pscustomobject]@{ PSTypeName = 'RePS.ExerciseData' Id = $data.Id Title = $data.Title Topic = $data.Topic Level = [int]$data.Level Platforms = @($data.Platforms) Tags = @($data.Tags) FunctionName = $data.FunctionName Prompt = $data.Prompt Hints = @($data.Hints) Author = $data.Author Folder = $folder StubPath = Join-Path $folder 'stub.ps1' SolutionPath = Join-Path $folder 'solution.ps1' TestPath = Join-Path $folder 'tests.Tests.ps1' } } $exercises = @($exercises | Sort-Object Topic, Level, Id) if ($Path -eq $script:ExercisesPath) { $script:ExerciseCache = $exercises } $exercises } function Resolve-RePSExercise { <# .SYNOPSIS Resolves an exercise by id, or the current exercise from progress when no id is given. #> [CmdletBinding()] param( [string]$Id, [hashtable]$Progress = (Read-RePSProgress) ) if (-not $Id) { $Id = $Progress.Current if (-not $Id) { throw 'No current exercise. Run Start-RePSExercise (alias: reps) first, or pass -Id.' } } $exercise = Get-RePSExerciseData | Where-Object Id -EQ $Id if (-not $exercise) { throw "Exercise '$Id' not found. Run Get-RePSExercise to list available exercises." } $exercise } function Assert-RePSPlatform { <# .SYNOPSIS Throws when an exercise does not support the current OS. #> [CmdletBinding()] param( [Parameter(Mandatory)] $Exercise ) $platform = Get-RePSPlatform if ($platform -notin $Exercise.Platforms) { throw "Exercise '$($Exercise.Id)' only runs on $($Exercise.Platforms -join ', '); this is $platform." } } function Open-RePSFile { <# .SYNOPSIS Opens a file in VS Code when the 'code' command is available. #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory)] [string]$Path ) if ($env:REPS_NO_OPEN) { return $false } $code = Get-Command -Name 'code' -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $code) { return $false } & $code.Source $Path $true } function Write-RePSPrompt { <# .SYNOPSIS Writes the exercise brief to the host. #> [CmdletBinding()] param( [Parameter(Mandatory)] $Exercise ) $stars = ('*' * $Exercise.Level).PadRight(3, '.') Write-Host '' Write-Host " $($Exercise.Title)" -ForegroundColor Cyan Write-Host " [$($Exercise.Topic)] level $stars id: $($Exercise.Id)" -ForegroundColor DarkGray Write-Host '' foreach ($line in ($Exercise.Prompt.Trim() -split '\r?\n')) { Write-Host " $line" } Write-Host '' } function New-RePSWorkspaceContent { <# .SYNOPSIS Builds the workspace file text: a comment header with the brief, followed by the stub. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] $Exercise ) $promptLines = ($Exercise.Prompt.Trim() -split '\r?\n') -replace '#>', '# >' $header = @( '<#' " rePS exercise: $($Exercise.Title)" " Id: $($Exercise.Id) | Topic: $($Exercise.Topic) | Level: $($Exercise.Level)" '' ($promptLines | ForEach-Object { " $_".TrimEnd() }) '' ' Check your work: Test-RePSExercise (alias: rept)' ' Need a nudge? Get-RePSHint (alias: reph)' '#>' '' ) -join [Environment]::NewLine $header + (Get-Content -LiteralPath $Exercise.StubPath -Raw) } |