feat(tui): 配置编辑器循环 + 挂进 Edit-Config(第二轮第三块·完成)
装配:Get-BakNRetConfigSetting(读)→ 菜单选设置 → Read-BakNRetLine(输入新值)→ Set-BakNRetConfigSetting(只改那一行)→ Save-BakNRetConfigFile(校验通过才原子替换 + 时间戳备份)。 输入行**从空开始**、当前值只作提示:预填当前值看着贴心,实际等于逼用户先删一遍(对 'Backups' 这种带引号的原文尤其别扭)。 校验用 Import-PowerShellDataFile:这份配置里没有动态表达式,能被它读回来是个**强校验** —— 比"语法能不能过"更强,它还能抓出"值是合法语法、但结构不对"。它只接受文件路径,所以先把候选文本写到临时文件再读。 Edit-Config 的 -Target Settings 与菜单里的"设置"都挂上了同一个编辑器;-Target Catalog 仍明确报"还没做(第三轮)"。 判据 9 条:改一个值后只动一行、注释行数不变、新值落盘、留下时间戳备份且备份里是原文;取消时不写盘也不产生备份。 验收:test.ps1 9/9 全绿(5.1 与 7)。
This commit is contained in:
1 parent
92bbc995c4
commit
4aa44e8965
5 files changed
+143
-2
No files matched your search
@@ -19,6 +19,7 @@
|
|||||||
CompatiblePSEditions = @('Desktop', 'Core')
|
CompatiblePSEditions = @('Desktop', 'Core')
|
||||||
|
|
||||||
FunctionsToExport = @(
|
FunctionsToExport = @(
|
||||||
|
'Invoke-BakNRetConfigEditor',
|
||||||
'Read-BakNRetLine',
|
'Read-BakNRetLine',
|
||||||
'Get-BakNRetConfigSetting',
|
'Get-BakNRetConfigSetting',
|
||||||
'Set-BakNRetConfigSetting',
|
'Set-BakNRetConfigSetting',
|
||||||
|
|||||||
@@ -173,7 +173,10 @@
|
|||||||
|
|
||||||
. (Join-Path $PSScriptRoot 'Public\Read-BakNRetLine.ps1')
|
. (Join-Path $PSScriptRoot 'Public\Read-BakNRetLine.ps1')
|
||||||
|
|
||||||
|
. (Join-Path $PSScriptRoot 'Public\Invoke-BakNRetConfigEditor.ps1')
|
||||||
|
|
||||||
Export-ModuleMember -Function @(
|
Export-ModuleMember -Function @(
|
||||||
|
'Invoke-BakNRetConfigEditor',
|
||||||
'Read-BakNRetLine',
|
'Read-BakNRetLine',
|
||||||
'Get-BakNRetConfigSetting',
|
'Get-BakNRetConfigSetting',
|
||||||
'Set-BakNRetConfigSetting',
|
'Set-BakNRetConfigSetting',
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
function Invoke-BakNRetConfigEditor {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
配置编辑器:列出单行标量设置 → 选一个 → 输入新值 → 校验后保存(并留时间戳备份)。
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
装配:Get-BakNRetConfigSetting(读)→ Invoke-BakNRetMenu(选)→ Read-BakNRetLine(输入)
|
||||||
|
→ Set-BakNRetConfigSetting(只改那一行)→ Save-BakNRetConfigFile(校验通过才原子替换)。
|
||||||
|
|
||||||
|
输入行**从空开始**、当前值只作为提示显示:预填当前值看着贴心,实际上等于逼用户先删一遍
|
||||||
|
(对 `'Backups'` 这种带引号的原文尤其别扭)。
|
||||||
|
|
||||||
|
校验用 Import-PowerShellDataFile:这份配置里没有动态表达式,能被它读进来是个**强校验**
|
||||||
|
—— 比"语法能不能过"更强,它还能抓出"值是合法语法、但结构不对"。它只接受文件路径,所以
|
||||||
|
先把候选文本写到临时文件再读。
|
||||||
|
|
||||||
|
取消(选设置或输入时按 Esc)→ 什么都不写、也不产生备份。
|
||||||
|
值没变 → 直接返回"没有变化",不做无意义的写盘与备份。
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)][string]$Path,
|
||||||
|
[Parameter(Mandatory = $true)]$Driver,
|
||||||
|
[switch]$WhatIf
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $Path)) {
|
||||||
|
return [pscustomobject]@{ Saved = $false; Changed = $false; Error = "找不到配置文件:$Path"; BackupPath = $null }
|
||||||
|
}
|
||||||
|
|
||||||
|
$text = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
|
||||||
|
$rows = @(Get-BakNRetConfigSetting -Text $text)
|
||||||
|
|
||||||
|
if ($rows.Count -eq 0) {
|
||||||
|
return [pscustomobject]@{ Saved = $false; Changed = $false; Error = '配置里没有可编辑的单行标量设置。'; BackupPath = $null }
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = @(foreach ($row in $rows) {
|
||||||
|
[pscustomobject]@{ Key = $row.Path; Text = ('{0,-26} = {1}' -f $row.Path, $row.Value) }
|
||||||
|
})
|
||||||
|
|
||||||
|
$picked = Invoke-BakNRetMenu -Title ('配置:{0}' -f $Path) -Items $items -Driver $Driver -MultiSelect $false -Hint '↑↓ 选设置 回车 改值 Esc 取消'
|
||||||
|
if ($picked.Action -ne 'confirm') {
|
||||||
|
return [pscustomobject]@{ Saved = $false; Changed = $false; Error = ''; BackupPath = $null }
|
||||||
|
}
|
||||||
|
|
||||||
|
$setting = $rows | Where-Object { $_.Path -eq [string]$picked.Chosen.Key } | Select-Object -First 1
|
||||||
|
|
||||||
|
Write-BakNRetAt -X 0 -Y 0 -Text ("{0} 当前是 {1};输入新值后回车确认,Esc 取消(留空表示空串):" -f $setting.Path, $setting.Value) -ForegroundColor Cyan
|
||||||
|
$newValue = Read-BakNRetLine -Driver $Driver
|
||||||
|
if ($null -eq $newValue) {
|
||||||
|
return [pscustomobject]@{ Saved = $false; Changed = $false; Error = ''; BackupPath = $null }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($newValue -eq $setting.Value) {
|
||||||
|
return [pscustomobject]@{ Saved = $false; Changed = $false; Error = '值没有变化,没有要保存的内容。'; BackupPath = $null }
|
||||||
|
}
|
||||||
|
|
||||||
|
$newText = Set-BakNRetConfigSetting -Text $text -Path $setting.Path -Value $newValue
|
||||||
|
|
||||||
|
$validate = {
|
||||||
|
param($candidate)
|
||||||
|
$probePath = Join-Path ([System.IO.Path]::GetTempPath()) ('bnr-cfg-' + [guid]::NewGuid().ToString('N').Substring(0, 8) + '.psd1')
|
||||||
|
try {
|
||||||
|
[System.IO.File]::WriteAllText($probePath, $candidate, (New-Object System.Text.UTF8Encoding($true)))
|
||||||
|
$null = Import-PowerShellDataFile -Path $probePath -ErrorAction Stop
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return @('改完之后读不回来:' + $_.Exception.Message)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Remove-Item -LiteralPath $probePath -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$saved = Save-BakNRetConfigFile -Path $Path -Text $newText -Validate $validate `
|
||||||
|
-BackupDirectory (Join-Path (Split-Path -Parent $Path) 'logs\config-backups') -WhatIf:$WhatIf
|
||||||
|
|
||||||
|
return [pscustomobject]@{
|
||||||
|
Saved = $saved.Saved
|
||||||
|
Changed = $true
|
||||||
|
Error = $saved.Error
|
||||||
|
BackupPath = $saved.BackupPath
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
-2
@@ -56,8 +56,18 @@ if ($Target -eq 'List') {
|
|||||||
exit (Invoke-BakNRetListEditorEntry -Path $ListPath -Script $InputScript)
|
exit (Invoke-BakNRetListEditorEntry -Path $ListPath -Script $InputScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($Target -eq 'Settings') {
|
||||||
|
$configPath = Join-Path $PSScriptRoot 'BackupConfig.psd1'
|
||||||
|
$driver = New-BakNRetInputDriver -Script $InputScript -NonInteractive:$([bool]$InputScript)
|
||||||
|
$saved = Invoke-BakNRetConfigEditor -Path $configPath -Driver $driver
|
||||||
|
if ($saved.Saved) { Write-Host ("已保存:{0}" -f $configPath) -ForegroundColor Green; Write-Host ("原文件备份:{0}" -f $saved.BackupPath) -ForegroundColor DarkGray; exit 0 }
|
||||||
|
if ($saved.Error) { Write-Host $saved.Error -ForegroundColor Yellow; exit 1 }
|
||||||
|
Write-Host '没有改动。' -ForegroundColor DarkGray
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
if ($Target) {
|
if ($Target) {
|
||||||
Write-Host ("{0} 的编辑器还没做:清单已完成;设置与名录分别是第二轮与第三轮的工作。" -f $Target) -ForegroundColor Yellow
|
Write-Host ("{0} 的编辑器还没做:清单与设置已完成;名录是第三轮的工作。" -f $Target) -ForegroundColor Yellow
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,5 +92,15 @@ if ($picked.Chosen.Key -eq 'List') {
|
|||||||
exit (Invoke-BakNRetListEditorEntry -Path $ListPath -Script $InputScript)
|
exit (Invoke-BakNRetListEditorEntry -Path $ListPath -Script $InputScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host ("{0} 的编辑器还没做。" -f $picked.Chosen.Key) -ForegroundColor Yellow
|
if ($picked.Chosen.Key -eq 'Settings') {
|
||||||
|
$configPath = Join-Path $PSScriptRoot 'BackupConfig.psd1'
|
||||||
|
$driver2 = New-BakNRetInputDriver -Script $InputScript -NonInteractive:$([bool]$InputScript)
|
||||||
|
$saved2 = Invoke-BakNRetConfigEditor -Path $configPath -Driver $driver2
|
||||||
|
if ($saved2.Saved) { Write-Host ("已保存:{0}" -f $configPath) -ForegroundColor Green; exit 0 }
|
||||||
|
if ($saved2.Error) { Write-Host $saved2.Error -ForegroundColor Yellow; exit 1 }
|
||||||
|
Write-Host '没有改动。' -ForegroundColor DarkGray
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ("{0} 的编辑器还没做(名录是第三轮的工作)。" -f $picked.Chosen.Key) -ForegroundColor Yellow
|
||||||
exit 1
|
exit 1
|
||||||
@@ -1786,6 +1786,38 @@ Test-Case '输入行:字符追加、Backspace 删除、Esc 取消返回 $null
|
|||||||
Assert-Equal 'k' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:K', 'Enter'))) '字母按小写存(与键名归一化一致)'
|
Assert-Equal 'k' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:K', 'Enter'))) '字母按小写存(与键名归一化一致)'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test-Case '配置编辑器:脚本驱动改一个值,只动那一行且留下备份;取消不写盘' {
|
||||||
|
$dir = Join-Path $env:TEMP ("bnr-cfged-" + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||||
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
||||||
|
$file = Join-Path $dir 'BackupConfig.psd1'
|
||||||
|
Copy-Item -LiteralPath (Join-Path (Split-Path -Parent $PSScriptRoot) 'BackupConfig.psd1') -Destination $file
|
||||||
|
$original = [System.IO.File]::ReadAllText($file)
|
||||||
|
$backupDir = Join-Path $dir 'logs\config-backups'
|
||||||
|
try {
|
||||||
|
# MinFreeSpaceGB 是文件里第 5 个标量设置:下移四次选中它,输入 9
|
||||||
|
$keys = @('Down', 'Down', 'Down', 'Down', 'Enter', 'Char:9', 'Enter')
|
||||||
|
$result = Invoke-BakNRetConfigEditor -Path $file -Driver (New-BakNRetInputDriver -Script $keys)
|
||||||
|
Assert-Equal $true $result.Saved '应当保存'
|
||||||
|
$before = @($original -split "`n")
|
||||||
|
$after = @([System.IO.File]::ReadAllText($file) -split "`n")
|
||||||
|
Assert-Equal 2 @(Compare-Object -ReferenceObject $before -DifferenceObject $after).Count '只应当有一行不同'
|
||||||
|
Assert-Equal @($before | Where-Object { $_.TrimStart().StartsWith('#') }).Count @($after | Where-Object { $_.TrimStart().StartsWith('#') }).Count '注释行数不变'
|
||||||
|
Assert-True (([System.IO.File]::ReadAllText($file)) -match 'MinFreeSpaceGB = 9') '新值写进去了'
|
||||||
|
Assert-True (Test-Path $result.BackupPath) '应当留下时间戳备份'
|
||||||
|
Assert-Equal $original ([System.IO.File]::ReadAllText($result.BackupPath)) '备份里是原文'
|
||||||
|
|
||||||
|
# 取消(选设置时 Esc):不写盘、不产生新备份
|
||||||
|
$countBefore = @(Get-ChildItem $backupDir -File | Measure-Object).Count
|
||||||
|
$contentBefore = [System.IO.File]::ReadAllText($file)
|
||||||
|
$cancelled = Invoke-BakNRetConfigEditor -Path $file -Driver (New-BakNRetInputDriver -Script @('Esc'))
|
||||||
|
Assert-Equal $false $cancelled.Saved '取消不该保存'
|
||||||
|
Assert-Equal $contentBefore ([System.IO.File]::ReadAllText($file)) '取消后内容不变'
|
||||||
|
Assert-Equal $countBefore @(Get-ChildItem $backupDir -File | Measure-Object).Count '取消不该新增备份'
|
||||||
|
} finally {
|
||||||
|
Remove-Item -LiteralPath $dir -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
|||||||
Reference in new issue
Block a user