RustDsc.psm1
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. using namespace System.Collections.Generic #region Functions function Assert-Cargo { # Refresh session $path value before invoking 'cargo' $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'User') if (-not (Get-Command 'cargo' -CommandType Application -ErrorAction Ignore)) { throw 'Cargo is not installed. Please install Rust and Cargo to use this module.' } } function Invoke-Cargo { param ( [Parameter(Mandatory = $true)] [string[]]$Arguments ) $out = & cargo @Arguments 2>&1 # Capture both stdout and stderr if ($LASTEXITCODE -ne 0) { throw "Cargo command failed with exit code $LASTEXITCODE`: $out" } return $out } function Get-InstalledCargoCrates { $cargoFile = if ($IsWindows) { Join-Path $env:USERPROFILE -ChildPath '.cargo' '.crates2.json' } else { Join-Path -Path $HOME -ChildPath '.cargo' '.crates2.json' } $cargoInstalledPackages = [List[hashtable[]]]::new() if (Test-Path -Path $cargoFile) { if (Test-Json -Path $cargoFile) { $installedPackages = (Get-Content -Path $cargoFile -Raw | ConvertFrom-Json).installs foreach ($k in $installedPackages.psobject.properties) { $split = $k.Name -split ' ' $package = @{ Name = $split[0] Version = $split[1] Features = @($k.Value.features) } $cargoInstalledPackages.Add($package) } } } if ($null -eq $cargoInstalledPackages -or $cargoInstalledPackages.Count -eq 0) { # fallback to 'cargo install --list' if .crates2.json is not available $result = Invoke-Cargo -Arguments @('install', '--list') # go through the output of 'cargo install --list' $lines = $result -split "`n" foreach ($line in $lines) { if ($line -match '^([^\s]+)\s+v(.+):') { $package = @{ Name = $matches[1] Version = $matches[2] Features = @() } $cargoInstalledPackages.Add($package) } } } return $cargoInstalledPackages } function Install-CargoCrate { param ( [Parameter(Mandatory = $true)] [string]$CrateName, [Parameter()] [string]$Version, [Parameter()] [string[]]$Features, [Parameter()] [bool]$Force = $false ) $arguments = @('install', $CrateName) if (-not([string]::IsNullOrEmpty($Version))) { $arguments += @('--version', $Version) } # Handle features if ($null -ne $Features -and $Features.Count -gt 0) { $arguments += @('--features', ($Features -join ',')) } else { # If no specific features are provided, assume all features $arguments += '--all-features' } # Handle force flag if ($Force) { $arguments += '--force' } $arguments += '--quiet' Write-Verbose -Message "Executing 'cargo $($arguments -join ' ')'" return Invoke-Cargo -Arguments $arguments } function Uninstall-CargoCrate { param ( [Parameter(Mandatory = $true)] [string]$CrateName ) $arguments = @('uninstall', $CrateName) Write-Verbose -Message "Executing 'cargo $($arguments -join ' ')'" return Invoke-Cargo -Arguments $arguments } function Test-CrateInstalled { param ( [Parameter(Mandatory = $true)] [string]$CrateName, [Parameter()] [string]$Version ) try { # Check global installation $installedCrates = Get-InstalledCargoCrates foreach ($crate in $installedCrates) { if ($crate.Name -eq $CrateName) { if ([string]::IsNullOrEmpty($Version) -or $crate.Version -eq $Version) { return @{ Installed = $true Version = $crate.Version Features = $crate.Features } } else { return @{ Installed = $false Version = $crate.Version Features = $crate.Features } } } } } catch { return @{ Installed = $false Version = $null Features = @() } } } #endregion Functions #region DSCResources <# .SYNOPSIS The `CargoToolInstall` DSC Resource allows you to manage the installation and removal of Rust crates using Cargo. This resource ensures that the specified Rust crate is in the desired state. .PARAMETER Exist Specifies whether the Rust crate should exist (be installed) or not. The default value is $true. .PARAMETER CrateName The name of the Rust crate to manage. This is a key property. .PARAMETER Version The version of the Rust crate to install. If not specified, the latest version will be installed. .PARAMETER Features A list of features to enable when installing the crate. If not specified, all features will be enabled using --all-features. .PARAMETER Force Force overwriting existing crates or binaries. The default value is $false. .EXAMPLE PS C:\> Invoke-DscResource -ModuleName RustDsc -Name CargoToolInstall -Method Set -Property @{ CrateName = 'bat' } This example installs the Rust crate 'bat' globally with all features enabled. .EXAMPLE PS C:\> Invoke-DscResource -ModuleName RustDsc -Name CargoToolInstall -Method Set -Property @{ CrateName = 'tokio'; Version = '1.0.0' } This example installs the Rust crate 'tokio' version 1.0.0 globally with all features enabled. .EXAMPLE PS C:\> Invoke-DscResource -ModuleName RustDsc -Name CargoToolInstall -Method Set -Property @{ CrateName = 'bat'; Features = @('minimal_application') } This example installs the Rust crate 'bat' globally with only the 'minimal_application' feature enabled. .EXAMPLE PS C:\> Invoke-DscResource -ModuleName RustDsc -Name CargoToolInstall -Method Set -Property @{ CrateName = 'ripgrep'; Force = $true } This example installs the Rust crate 'ripgrep' globally with force overwriting enabled. #> [DSCResource()] class CargoToolInstall { [DscProperty()] [bool]$Exist = $true [DscProperty(Key, Mandatory = $true)] [string]$CrateName [DscProperty()] [string]$Version [DscProperty()] [string[]]$Features [DscProperty()] [bool]$Force = $false [DscProperty(NotConfigurable)] [string]$InstalledVersion [CargoToolInstall] Get() { Assert-Cargo $currentState = [CargoToolInstall]::new() $currentState.CrateName = $this.CrateName $currentState.Version = $this.Version $currentState.Features = $this.Features $currentState.Force = $this.Force $crateInfo = Test-CrateInstalled -CrateName $this.CrateName -Version $this.Version if ($null -eq $crateInfo) { $currentState.Exist = $false } else { $currentState.InstalledVersion = $crateInfo.Version $currentState.Features = $crateInfo.Features if (-not([string]::IsNullOrEmpty($this.Version)) -and (-not([string]::IsNullOrEmpty($crateInfo.Version)) -and ($crateInfo.Version -ne $this.Version))) { $currentState.Exist = $false } else { $currentState.Version = $crateInfo.Version } } return $currentState } [bool] Test() { $currentState = $this.Get() return $this.Exist -eq $currentState.Exist } [void] Set() { $inDesiredState = $this.Test() if ($this.Exist) { if (-not $inDesiredState) { Install-CargoCrate -CrateName $this.CrateName -Version $this.Version -Features $this.Features -Force $this.Force } } else { if (-not $inDesiredState) { Uninstall-CargoCrate -CrateName $this.CrateName } } } static [CargoToolInstall[]] Export() { $installedCrates = Get-InstalledCargoCrates $out = [List[CargoToolInstall]]::new() foreach ($installedCrate in $installedCrates) { $crate = [CargoToolInstall]@{ CrateName = $installedCrate.Name Version = $installedCrate.Version Features = $installedCrate.Features Force = $false # Cannot determine force from install list Exist = $true InstalledVersion = $installedCrate.Version } $out.Add($crate) } return $out } } #endregion DSCResources # SIG # Begin signature block # MIIoOQYJKoZIhvcNAQcCoIIoKjCCKCYCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCmRUaNLV4MKllb # WFLr/MJjOwF1aR2JDuTPZfxWmLKDGqCCDYUwggYDMIID66ADAgECAhMzAAAEhJji # EuB4ozFdAAAAAASEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjUwNjE5MTgyMTM1WhcNMjYwNjE3MTgyMTM1WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDtekqMKDnzfsyc1T1QpHfFtr+rkir8ldzLPKmMXbRDouVXAsvBfd6E82tPj4Yz # aSluGDQoX3NpMKooKeVFjjNRq37yyT/h1QTLMB8dpmsZ/70UM+U/sYxvt1PWWxLj # MNIXqzB8PjG6i7H2YFgk4YOhfGSekvnzW13dLAtfjD0wiwREPvCNlilRz7XoFde5 # KO01eFiWeteh48qUOqUaAkIznC4XB3sFd1LWUmupXHK05QfJSmnei9qZJBYTt8Zh # ArGDh7nQn+Y1jOA3oBiCUJ4n1CMaWdDhrgdMuu026oWAbfC3prqkUn8LWp28H+2S # LetNG5KQZZwvy3Zcn7+PQGl5AgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUBN/0b6Fh6nMdE4FAxYG9kWCpbYUw # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwNTM2MjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AGLQps1XU4RTcoDIDLP6QG3NnRE3p/WSMp61Cs8Z+JUv3xJWGtBzYmCINmHVFv6i # 8pYF/e79FNK6P1oKjduxqHSicBdg8Mj0k8kDFA/0eU26bPBRQUIaiWrhsDOrXWdL # m7Zmu516oQoUWcINs4jBfjDEVV4bmgQYfe+4/MUJwQJ9h6mfE+kcCP4HlP4ChIQB # UHoSymakcTBvZw+Qst7sbdt5KnQKkSEN01CzPG1awClCI6zLKf/vKIwnqHw/+Wvc # Ar7gwKlWNmLwTNi807r9rWsXQep1Q8YMkIuGmZ0a1qCd3GuOkSRznz2/0ojeZVYh # ZyohCQi1Bs+xfRkv/fy0HfV3mNyO22dFUvHzBZgqE5FbGjmUnrSr1x8lCrK+s4A+ # bOGp2IejOphWoZEPGOco/HEznZ5Lk6w6W+E2Jy3PHoFE0Y8TtkSE4/80Y2lBJhLj # 27d8ueJ8IdQhSpL/WzTjjnuYH7Dx5o9pWdIGSaFNYuSqOYxrVW7N4AEQVRDZeqDc # fqPG3O6r5SNsxXbd71DCIQURtUKss53ON+vrlV0rjiKBIdwvMNLQ9zK0jy77owDy # XXoYkQxakN2uFIBO1UNAvCYXjs4rw3SRmBX9qiZ5ENxcn/pLMkiyb68QdwHUXz+1 # fI6ea3/jjpNPz6Dlc/RMcXIWeMMkhup/XEbwu73U+uz/MIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAASEmOIS4HijMV0AAAAA # BIQwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIHKk # w/QM9PJQJr8zLdAkhFKuSUpeqQ/+rGVXagQAZ89+MEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAMGTCTLmp9khXubLtSl7SSN02IoAVTBOyXC09 # c5x0c+m8fRQTQDpEBK+G0QWaGD8EzeX7hO4o+gfUTXJTVMhHxJyeDwpukXbhwZwO # fCiy6zZP9lfTu9ObvptvO84pzn0uFZVFbpepHdcwTZ1MN3XFtkAZZqr6h3MFXQyH # /5o2XnDpPRK9OsKIBXGChHJ5cpOybqsA9O4OT+eNHosjZkofrdpUqQyGMrDKCrxB # JpksB1H5Vj45ZU5LC6PjQZgBk/IlC/w1rDGB5I764GXH4S3EzBe8RObvXH8SY3EV # knF+Bi2DGX0MLguC/lLbbEfHsiUA9f+VK0Lvv3pkAzO4g4ZOqqGCF5QwgheQBgor # BgEEAYI3AwMBMYIXgDCCF3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCDjJ4HeS2kmta2Emm3d1yMVLMQDaYBe8Ep9 # BJfdiTLmZAIGaF2vXMssGBMyMDI1MDgwNzAwMDY1Ni41NDdaMASAAgH0oIHRpIHO # MIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL # ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxk # IFRTUyBFU046OEQwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l # LVN0YW1wIFNlcnZpY2WgghHqMIIHIDCCBQigAwIBAgITMwAAAg0Nd757No9/4wAB # AAACDTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx # MDAeFw0yNTAxMzAxOTQzMDFaFw0yNjA0MjIxOTQzMDFaMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OEQwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Uw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCxf6gH3fH3wgmFN5TV8zRF # /N0TJguWUYCrQZPUmwA+QhSySNp7kiGmFZd4b5zsAfN0Wh+PzIJvYsVMgVCaZcbV # r/DJBfexwnQfc+fgIjOiAzYSjg7EtSOdWoLk81b/mGiGIBC++fLcSAzbZO3KtW4P # RKOSsdD/5eRdtNca/Ed4EAcUT32zAGS9Sq//4kDT92KEzRNXJj8z3NDL4oGGzCQM # vA83tQG5mrnepxF0OsNfKKHYHMqjyOEP5pTgKfT5XMfz0sEG6ARAjlXJ79SG/joe # uHh8TqC+cJMry9wB7ZLrdMAFy8rHN3W1+kkpw47Ko+9ize2ble+P5jMaqufK033B # u+2FXVSKphil2j0qBUWpn5vBtf2W+gsVqydA+eseBHfYxcDZ4+5oRoyDAg0tW9f7 # 9vOAv91P4bTzG+BZPBbDMzSDwmj8ASKDlVwruTeF1em7NWiedWAB+29gFH/c/NN1 # uTQLvwGDIOw1DcLnCD0VXNL7mOvifYvNWugTAHcMFLVlA1jeOH35E/IW9qcKKqra # h7LyJax/6M5UHswQugGgLriiMNEvz3IqW+AiIJ097iYzMGzsDqbLSUztIjDEt9xf # IHHUs/p3j9Bkr2bPP1v4z8vp/45Ck3mfFbW2F0EtjOCnGPMrJNjjGhEG9zAK1105 # Bg2kJ7Rn8WTWO5IbD/rDtQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFBWXjpDmDgNr # TsISj26SjU1/YMOAMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8G # A1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBs # BggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy # MDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH # AwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAcH8gT42wVQ8GQ # Z+MHIXNJ+d4dQn0+vPG/AdFvTxk/tvOOkA2i7hnoEOEFcSbzS5EdIVCMi5Y5EiWT # 8hEJkztdeq5hXtUWsPY+2lYSU9HdhKDfRDfjwVZ9kfCthrLRC3jw9Fah5MAGI9MH # SETo9r7+cux8AUqQ3hmaM2jmTNWvrFOLbO01B1ciNGbvE2xK+rbzBBh/uWd3k30p # au6Lp0azg7rDYGMGv8jWotILfpKMBeCQoufMsI/gzxo4+k9dqYBkAwMk7h1hf23y # cb5WngSMQV/Uxl3Sxbg+64KK6GCsTSG6z7zNTgbL69PMGfwV2cnawY95Iy2cgJ6c # bMORRUcYzvsuUd8oEQ87cW4XqqBLrheewJjROT6YyWrQ2oQ+jzGK2WJoGNnfanmN # fqQnVKpi320onag95LMFjj8BwrflYsO9kEOiy7I5UngPBmF+RHSCv2hFSr8nK7gt # uiy9SUOKP6FbQOzyMRvJ3UxsmrH38477XzETb/tZLAj10TdYFfkjkFeFjlb3iMTS # s/VrJSF0r0vON/oxZqKCY8WZez+uQP0Try0QQ9wRp5D2FYJ8E1uIX/LvwuFkBdWf # 7X7qlb+pzdvPpSAcaCgBIWTlMn2bWgkU5uPzxRPHh/0u+FI7/eRCZGbLM2qnn3yX # QvO/h9wQm8pIABRAvodaiV0bVmHbETCCB3EwggVZoAMCAQICEzMAAAAVxedrngKb # SZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj # YXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIy # NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXI # yjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjo # YH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1y # aa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v # 3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pG # ve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viS # kR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYr # bqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlM # jgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSL # W6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AF # emzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIu # rQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIE # FgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWn # G1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEW # M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5 # Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi # AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV # 9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js # Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAx # MC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2 # LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv # 6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZn # OlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1 # bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4 # rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU # 6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDF # NLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/ # HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdU # CbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKi # excdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTm # dHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZq # ELQdVTNYs6FwZvKhggNNMIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp # Y2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjhEMDAtMDVF # MC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMK # AQEwBwYFKw4DAhoDFQB7LCwoj6G3nQ7Oxhl/pfne4yATPaCBgzCBgKR+MHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7D43vzAi # GA8yMDI1MDgwNjIwMjExOVoYDzIwMjUwODA3MjAyMTE5WjB0MDoGCisGAQQBhFkK # BAExLDAqMAoCBQDsPje/AgEAMAcCAQACAjt2MAcCAQACAhOZMAoCBQDsP4k/AgEA # MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI # AgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBAEAVYH/s9kDU2227R0Pn1LAyYV9x # brW9N0//ZJragCjsPkjf0eUPUh3fyt8Flpej0I9G9APPvZ+OFV3xpV70nD2PuBPI # AwzKiUDgC9uhLIP7fpo2b3Rsm0DtG0wuvVhIfRIt8+Vuz03m+Q5AVhxTR6ZGQxF/ # BAs8E2HAvm4mVIiKw5DRSFEtyfpJ1Z1BW7rVRMSQUk7GTZjRZ8uMiRArbCGPebZe # FkwgPpAB+RWqkcmmiJ7DnOuriGMhSdbCkzIFLu9DdOF8AMrMwoIsF2IbTiIwfOvi # 91ewg4FjugJRLJeTOXbEvxryCZiXhicYsKKQxLMTOEUpVCKcAtmRpzF0TIYxggQN # MIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAg0N # d757No9/4wABAAACDTANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0G # CyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDxVlN/HEuvQhLQnJBxdquhHkwT # iQFsug3xAwtXUDN1pjCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIGPqB5Ts # JGqI8OuknBKtSvb0Ffq6w5NSs5veTVwka/hAMIGYMIGApH4wfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAINDXe+ezaPf+MAAQAAAg0wIgQgt+29uP7K # YMfZOkYPCh0k7FLIQCLw0wexQ/at1LI23VUwDQYJKoZIhvcNAQELBQAEggIAXmal # JpDu6ZFRK/my5QFkurREw64wafucNLGK7xf1WuBQq5RMECNAp0a4ABt/oBLhQZzJ # OxCx2zd7MeEDEF4Fw8CwL9Mht96kH7J2ZTAa5WYgVMSfGZfdus1pi+zh9M9QwMkj # KGRYjA4AdADZtoQIIEqsCVf8UaEyqyKwIY5FpcTekFWiHbgnt/Hka93S9PIgRcRS # 0JOkhveGB5ElK5q+mvAhgMre9OQuCkau+4k9jHmSFDPbriE+BKcmb1rgY5v9dejK # aDPV+A6+STScJLxOKK9ChVTN1+zB5dNWfSgEXstqWGpc+4sINSlx8+rW9ddZc0Vt # uhy67kC4kFPTp2I3+jh1kuV/XxGOizipgNJGaNNOR9Ol05ltdLndiX+bcNKYEk8y # UJ1RifwVUUSMn3wgoBBB1DxnFQhFpW4Vmc2zsU+/Y2HetPOJS6J9Oj/qqDRRgaQv # FzOgTHWFEG+Z75w2qWABmKbknMK6SN/r64yzkcfkWWIhwHuozoj3iJdaUjWJHOyK # q043guLFhNeEI3lIOZlnNopMuELiQ58vi9mlklji41qJGsTLYIZ15V3qSusW2g9f # 9neAxN1vpw/djlzH9/D4H0mngpimgkNWgq0rS+l1oUYWxk+i4Buc5MtJ4c/5Xzy3 # PyGUIwOWOgYVsKMyad4S1f5Ae+XnpljAGSQMqbQ= # SIG # End signature block |