281 lines
8.7 KiB
PowerShell
281 lines
8.7 KiB
PowerShell
function New-Option ([string]$Name, [switch]$Switch, [switch]$Boolean, [switch]$String, [switch]$List, $Constraints=$null) {
|
|
<#
|
|
.SYNOPSIS
|
|
Creates Option object
|
|
|
|
.DESCRIPTION
|
|
Option object is a virtual object represtnting typed command line option. These objects encapsulate escaping and
|
|
validation matters.
|
|
|
|
One and only one of the switches 'Switch', 'Boolean', 'String' or 'List' should be provided.
|
|
|
|
.PARAMETER Name
|
|
Option name as it appears in the command line.
|
|
|
|
.PARAMETER Switch
|
|
Use this switch to create valueless option (a switch).
|
|
|
|
.PARAMETER Boolean
|
|
Use this switch to create boolean option. Its value is always converted to "1" or "0"
|
|
|
|
.PARAMETER String
|
|
Use this switch to create string option. Its value will be properly quoted if necessary.
|
|
|
|
.PARAMETER List
|
|
Use this switch to create option with list value. Values will be put into command line using valid value delemiter (a comma)
|
|
|
|
.PARAMETER Constraints
|
|
When this parameter is specified, option values are limited to options from that list.
|
|
|
|
#>
|
|
|
|
$Option = New-Object -TypeName PSObject
|
|
|
|
# Fields
|
|
$Option | Add-Member NoteProperty Type -value $null
|
|
$Option | Add-Member NoteProperty Name -value $null
|
|
$Option | Add-Member NoteProperty AllowedValues -value $null
|
|
|
|
# Init
|
|
|
|
$Option | Add-Member ScriptMethod __init__ {
|
|
param([string]$Name, $Switch, $Boolean, $String, $List)
|
|
|
|
$this.Name = $Name
|
|
|
|
# With respect for our developers we do not check for double type selected
|
|
if ($Switch) {
|
|
AugmentOptionSwitch($this)
|
|
} elseif ($Boolean) {
|
|
AugmentOptionBoolean($this)
|
|
} elseif ($String) {
|
|
AugmentOptionString($this)
|
|
} elseif ($List) {
|
|
AugmentOptionList($this)
|
|
} else {
|
|
throw "Switch, Boolean, String or List option type must be provided for option '$Name'"
|
|
}
|
|
}
|
|
|
|
$Option | Add-Member ScriptMethod __post_init__ {
|
|
param($Constraints=$null)
|
|
if ($Constraints -ne $null) {
|
|
$this.AllowedValues = @()
|
|
$this.AllowedValues = $this.AllowedValues + $Constraints
|
|
} else {
|
|
$Constraints = $null
|
|
}
|
|
}
|
|
|
|
# Methods
|
|
|
|
$Option | Add-Member -Force ScriptMethod Validate {
|
|
if ($this.AllowedValues -ne $null) {
|
|
if (-not($this.AllowedValues -contains $this.Value)) {
|
|
$Cts = $this.AllowedValues -join ','
|
|
throw "Option '$($this.Name)' may have values ($Cts) but not '$($this.Value)'"
|
|
}
|
|
}
|
|
}
|
|
|
|
$Option | Add-Member -Force ScriptMethod ToString {
|
|
return "/$($this.Name)"
|
|
}
|
|
|
|
# invoke constructor
|
|
|
|
$Option.__init__($Name, $Switch, $Boolean, $String, $List)
|
|
$Option.__post_init__($Constraints)
|
|
|
|
return $Option
|
|
}
|
|
|
|
function AugmentOptionSwitch($Option) {
|
|
}
|
|
|
|
function AugmentOptionBoolean($Option) {
|
|
# Fields
|
|
$Option | Add-Member NoteProperty Value -value $false
|
|
|
|
# Methods
|
|
|
|
$Option | Add-Member -Force ScriptMethod ToString {
|
|
if ($this.Value) {
|
|
return "/$($this.Name)=1"
|
|
} else {
|
|
return "/$($this.Name)=0"
|
|
}
|
|
}
|
|
}
|
|
|
|
function AugmentOptionString($Option) {
|
|
# Fields
|
|
$Option | Add-Member NoteProperty Value -value ""
|
|
|
|
# Methods
|
|
|
|
$Option | Add-Member -Force ScriptMethod ToString {
|
|
$v = "$($this.Value)"
|
|
if ($v -match '.* .*') {
|
|
# TODO: Escape double quote characters if possible
|
|
return "/$($this.Name)=`"$v`""
|
|
} else {
|
|
return "/$($this.Name)=$v"
|
|
}
|
|
}
|
|
}
|
|
|
|
function AugmentOptionList($Option) {
|
|
# Fields
|
|
$Option | Add-Member NoteProperty Value -value @()
|
|
|
|
# Methods
|
|
|
|
$Option | Add-Member -Force ScriptMethod Validate {
|
|
if ($this.AllowedValues -ne $null) {
|
|
foreach ($V in $this.Value) {
|
|
if (-not($this.AllowedValues -contains $V)) {
|
|
$Cts = $this.AllowedValues -join ','
|
|
throw "Option '$($this.Name)' may have values ($Cts) but not '$V'"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$Option | Add-Member -Force ScriptMethod ToString {
|
|
return "/$($this.Name)=$($this.Value -join ',')"
|
|
}
|
|
}
|
|
|
|
function New-OptionParser() {
|
|
<#
|
|
.SYNOPSIS
|
|
Creates OptionParser object.
|
|
|
|
.DESCRIPTION
|
|
OptionParser object leverages Option objects capabilities and builds valid command line using specified options.
|
|
An application may also be invoked with OptionParser.
|
|
|
|
#>
|
|
|
|
$OptionParser = New-Object -TypeName PSObject
|
|
|
|
# Fields
|
|
$OptionParser | Add-Member NoteProperty Options -value @{}
|
|
$OptionParser | Add-Member NoteProperty Defaults -value @{}
|
|
$OptionParser | Add-Member NoteProperty RequiredOptions -value @()
|
|
|
|
# Methods
|
|
|
|
$OptionParser | Add-Member ScriptMethod AddOption {
|
|
<#
|
|
.SYNOPSIS
|
|
Adds supported option into OptionParser.
|
|
|
|
.DESCRIPTION
|
|
OptionParser does not allow using unrecognized options. Use this method to fill OptionParser with recognized options
|
|
|
|
.PARAMETER Option
|
|
Option object
|
|
|
|
.PARAMETER Required
|
|
Required option switch
|
|
|
|
.PARAMETER Default
|
|
Option default value
|
|
#>
|
|
param($Option, [bool]$Required=$false, $Default=$null)
|
|
$this.Options.Add($Option.Name, $Option)
|
|
if ($Required) {
|
|
$this.RequiredOptions = $this.RequiredOptions + $Option.Name
|
|
if ($Option | Get-Member "Value") {
|
|
if ($Default) {
|
|
$this.Defaults.Add($Option.Name, $Default)
|
|
}
|
|
} else {
|
|
$this.Defaults.Add($Option.Name, $null)
|
|
}
|
|
}
|
|
}
|
|
|
|
$OptionParser | Add-Member ScriptMethod Parse {
|
|
<#
|
|
.SYNOPSIS
|
|
Parses supplied options and returns command line parameters array.
|
|
|
|
.DESCRIPTION
|
|
This method verifies that only supported options are provided, all mandatory options are in place,
|
|
all option meet constraints if any. Unspecified options with default values are added to command line.
|
|
So, mandatory option with default value never causes exception.
|
|
|
|
.PARAMETER Options
|
|
A hash map of options to parse. Option names should be mapped to corresponding values.
|
|
#>
|
|
param([hashtable]$Options)
|
|
|
|
$CommandLine = @()
|
|
foreach ($RequiredOptionName in $this.RequiredOptions) {
|
|
if (-not $Options.ContainsKey($RequiredOptionName)) {
|
|
$Default = $this.Defaults.Get_Item($RequiredOptionName)
|
|
if ($this.Defaults.ContainsKey($RequiredOptionName)) {
|
|
$Options.Add($RequiredOptionName, $this.Defaults.Get_Item($RequiredOptionName))
|
|
} else {
|
|
throw "Required option '$RequiredOptionName' is missing"
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($OptionName in $($Options.keys)) {
|
|
$Option = $this.Options.Get_Item($OptionName)
|
|
if ($Option -eq $null) {
|
|
throw "Option '$OptionName' is not allowed"
|
|
}
|
|
if ($Option | Get-Member "Value") {
|
|
$Option.Value = $Options.Get_Item($OptionName)
|
|
}
|
|
$Option.Validate()
|
|
$CommandLine = $CommandLine + $Option.ToString()
|
|
}
|
|
return $CommandLine
|
|
}
|
|
|
|
$OptionParser | Add-Member ScriptMethod ExecuteBinary {
|
|
param($Binary, [hashtable]$Options = @{}, $CommandLineSuffix = @())
|
|
<#
|
|
.SYNOPSIS
|
|
Executes binary with a command line constructed from provided options. An arbitrary suffix may be
|
|
appended to the command line.
|
|
|
|
.DESCRIPTION
|
|
This method uses OptionParser.Parse method to construct command line. If there a command line suffix
|
|
was supplied, it is appended to the end of command line. Normally command line suffix should contain
|
|
leading space character.
|
|
|
|
Method waits for executable process to complete and returns its exit code.
|
|
|
|
.PARAMETER Binary
|
|
Full or relative path to the executable to run.
|
|
|
|
.PARAMETER Options
|
|
A hash map of options to pass to the executable.
|
|
|
|
.PARAMETER CommandLineSuffix
|
|
Arbitrary command line suffix. Normally it shoud have leading space character.
|
|
#>
|
|
|
|
$Binary = Get-Item $Binary
|
|
$CommandLine = $this.Parse($Options)
|
|
if ($CommandLineSuffix) {
|
|
$CommandLine = $CommandLine + $CommandLineSuffix
|
|
}
|
|
|
|
Write-Log "Executing: $($Binary.FullName) $($CommandLine -join ' ')"
|
|
$process = [System.Diagnostics.Process]::Start($Binary, $CommandLine)
|
|
$process.WaitForExit()
|
|
$process.Refresh()
|
|
return $process.ExitCode
|
|
}
|
|
|
|
return $OptionParser
|
|
}
|