diff --git a/.github/workflows/powershell.yml b/.github/workflows/powershell.yml new file mode 100644 index 0000000..65efd1a --- /dev/null +++ b/.github/workflows/powershell.yml @@ -0,0 +1,160 @@ +name: PowerShell + +on: + push: + branches: [ "main", "master" ] + paths-ignore: + - 'docs/**' + - 'example/**' + - 'Videos/**' + - 'Changelog.md' + - 'README.md' + pull_request: + +permissions: + contents: read + +jobs: + build: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + name: Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # avoid shallow clone so nbgv can do its work. + + - name: Run PSScriptAnalyzer + uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f + with: + # Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options. + # The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules. + path: .\src + recurse: true + # Include your own basic security rules. Removing this option will run all the rules + includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText"' + output: results.sarif + + # Upload the SARIF file generated in the previous step + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + + - uses: dotnet/nbgv@f088059084cb5d872e9d1a994433ca6440c2bf72 + id: nbgv + + - name: Build + shell: pwsh + run: ./build.ps1 build ${{ steps.nbgv.outputs.VersionMajor }} ${{ steps.nbgv.outputs.VersionMinor }} ${{ steps.nbgv.outputs.BuildNumber }} ${{ steps.nbgv.outputs.VersionRevision }} ${{ steps.nbgv.outputs.PrereleaseVersionNoLeadingHyphen }} + + - name: Store build output + uses: actions/upload-artifact@v4 + with: + name: build + path: | + publish + retention-days: 1 + + test7-old: + permissions: + contents: read # for actions/checkout to fetch code + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + name: Test PowerShell 7 Past Versions + needs: Build + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/powershell:${{ matrix.pwshv }}-ubuntu-22.04 + strategy: + matrix: + pwshv: ['7.2', '7.3'] + + steps: + - uses: actions/checkout@v4 + + - name: Download build output + uses: actions/download-artifact@v4 + with: + name: build + path: publish + + - name: Test + shell: pwsh + run: ./build.ps1 test + + test7-current: + permissions: + contents: read # for actions/checkout to fetch code + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + name: Test PowerShell 7.4 + needs: Build + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/powershell:${{ matrix.pwshv }}-ubuntu-22.04 + strategy: + matrix: + pwshv: ['7.4'] + + steps: + - uses: actions/checkout@v4 + + - name: Download build output + uses: actions/download-artifact@v4 + with: + name: build + path: publish + + - name: Test + shell: pwsh + run: ./build.ps1 test + + test5: + permissions: + contents: read # for actions/checkout to fetch code + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + name: Test PowerShell 5 + needs: Build + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download build output + uses: actions/download-artifact@v4 + with: + name: build + path: publish + + - name: Test + shell: powershell + run: ./build.ps1 test + + publish: + permissions: + contents: read # for actions/checkout to fetch code + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + name: Publish + needs: [test7-current] + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/dotnet/sdk:8.0 + if: github.ref == 'refs/heads/main' + steps: + - uses: actions/checkout@v4 + + - name: Download build output + uses: actions/download-artifact@v4 + with: + name: build + path: publish + + - name: Publish + shell: pwsh + run: ./build.ps1 publish + env: + PSPublishApiKey: ${{ secrets.NUGETAPIKEY }} diff --git a/.gitignore b/.gitignore index 598418c..35594bf 100644 --- a/.gitignore +++ b/.gitignore @@ -215,3 +215,5 @@ pip-log.txt .mr.developer.cfg InstallLocally.ps1 CleanPSHumanizer.ps1 + +testResults.xml diff --git a/DoTests.ps1 b/DoTests.ps1 deleted file mode 100644 index fc35c84..0000000 --- a/DoTests.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -param( - [Switch]$UsePSCore -) - -function Install-PSCore { - param( - $pscoreVerion = '6.2.0-preview.3', - $os = 'win-x64' - ) - - $unZipPath = "$env:TEMP\pscore" - - if (!(Test-Path $unZipPath)) { - $outfile = "$env:TEMP\pscore.zip" - - $url = "https://github.com/PowerShell/PowerShell/releases/download/v$($pscoreVerion)/PowerShell-$($pscoreVerion)-$($os).zip" - - "Downloading PS Core" - Invoke-RestMethod $url -OutFile $outfile - - "Unzipping PS Core" - Expand-Archive -Path $outfile -DestinationPath $unZipPath -Force - - Remove-Item $outfile -ErrorAction SilentlyContinue - } - - "$unZipPath\pwsh.exe" -} - -if ($UsePSCore) { - $pwsh = Install-PSCore -os 'win-x64' - & $pwsh[-1] -} - -$PSVersionTable.PSVersion - -if ($null -eq (Get-Module -ListAvailable pester)) { - Install-Module -Name Pester -Repository PSGallery -Force -Scope CurrentUser -} - -$result = Invoke-Pester -Script $PSScriptRoot\__tests__ -Verbose -PassThru - -if ($result.FailedCount -gt 0) { - throw "$($result.FailedCount) tests failed." -} \ No newline at end of file diff --git a/FileSystem.types.ps1xml b/FileSystem.types.ps1xml deleted file mode 100644 index cb1683a..0000000 --- a/FileSystem.types.ps1xml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - System.DateTime - - - DateTime - - if ((& { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq "Date") - { - "{0}" -f $this.ToLongDateString() - } - elseif ((& { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq "Time") - { - "{0}" -f $this.ToLongTimeString() - } - else - { - "{0} {1}" -f $this.ToLongDateString(), $this.ToLongTimeString() - } - - - - - - System.IO.DirectoryInfo - - - Mode - - Microsoft.PowerShell.Commands.FileSystemProvider - Mode - - - - BaseName - - $this.Name - - - - PSStandardMembers - - - DefaultDisplayProperty - Name - - - - - - - System.IO.FileInfo - - - Mode - - Microsoft.PowerShell.Commands.FileSystemProvider - Mode - - - - VersionInfo - - [System.Diagnostics.FileVersionInfo]::GetVersionInfo($this.FullName) - - - - BaseName - - if ($this.Extension.Length -gt 0){$this.Name.Remove($this.Name.Length - $this.Extension.Length)}else{$this.Name} - - - - PSStandardMembers - - - DefaultDisplayPropertySet - - LastWriteTime - Length - Name - - - - - - - - System.Management.ManagementObject#root\cimv2\Win32_Process - - - ProcessName - Name - - - Handles - Handlecount - - - VM - VirtualSize - - - WS - WorkingSetSize - - - Path - $this.ExecutablePath - - - - \ No newline at end of file diff --git a/Humanizer.dll b/Humanizer.dll deleted file mode 100644 index 179a556..0000000 Binary files a/Humanizer.dll and /dev/null differ diff --git a/PowerShellHumanizer.Tests.ps1 b/PowerShellHumanizer.Tests.ps1 deleted file mode 100644 index 1f23b20..0000000 --- a/PowerShellHumanizer.Tests.ps1 +++ /dev/null @@ -1,132 +0,0 @@ -$here = Split-Path -Parent $MyInvocation.MyCommand.Path -$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.ps1', '.psd1' -ipmo "$here\$sut" -Force - -Describe 'Functions' { - Context 'Pluralize' { - It 'Should convert man to men' { - ConvertTo-Plural man | Should Be 'men' - } - It 'Should convert an array' { - $output = echo person man woman | ConvertTo-Plural - $output[0] | Should Be 'people' - $output[1] | Should Be 'men' - $output[2] | Should Be 'women' - } - } - Context 'Singularize' { - $output = echo people men women geese indicies oxen knives | ConvertTo-Singular - It 'Should convert to singles' { - $output[0] | Should Be 'person' - $output[1] | Should Be 'man' - $output[2] | Should Be 'woman' - $output[3] | Should Be 'goose' - $output[4] | Should Be 'indicy' - $output[5] | Should Be 'ox' - $output[6] | Should Be 'knife' - } - - } - Context 'Hyphenate' { - It 'Should convert to a hyphenated string' { - - "Continuing To Make Powershell A Bit More Human" | - ConvertTo-HyphenatedString | - Should Be 'continuing-to-make-powershell-a-bit-more-human' - } - - } - Context 'Number to ordinal words' { - It 'Should convert to words' { - ConvertTo-OrdinalWords 121 | Should Be 'hundred and twenty-first' - } - It 'Should convert a range to words' { - $output = 120..122 | ConvertTo-OrdinalWords - $output[0] | Should Be 'hundred and twentieth' - $output[1] | Should Be 'hundred and twenty-first' - $output[2] | Should Be 'hundred and twenty-second' - } - } -} - -Describe 'Type Extension Methods' { - Context 'Strings' { - It 'Should transform to Upper Case' { - 'then add nodes under it.'.Transform("UpperCase") | Should Be 'THEN ADD NODES UNDER IT.' - } - It 'Should convert to Title Case' { - 'then add nodes under it.'.ToTitleCase() | Should Be 'Then Add Nodes Under It.' - } - It 'Should convert from Title Case' { - 'FromTitleCase'.Underscore() | Should Be 'from_title_case' - } - It 'Should truncate words' { - 'then add nodes under it.'.Truncate(3,"Words") | Should Match 'then add nodes\W$' - } - It 'Should truncate characters' { - 'then add nodes under it.'.Truncate(3,"Characters") | Should Match 'th\W$' - } - It 'Should truncate with optional character' { - 'then add nodes under it.'.Truncate(7, "Characters", '-') | Should Be 'then ad-' - } - It 'Should Dehumanize' { - 'then add nodes under it.'.Dehumanize() | Should Be 'ThenAddNodesUnderIt' - } - It 'Should provide quanity: # word' { - 'string'.ToQuantity(50) | Should Be '50 strings' - } - It 'Should provide quantity: words' { - 'string'.ToQuantity(50, "Words") | Should Be 'fifty strings' - } - It 'Should convert Year to roman numerals' { - (Get-Date).Year.ToRoman() | Should Be 'MMXVI' - } - } - Context 'Integers' { - It 'Should ordanalize' { - (3).Ordinalize() | Should Be '3rd' - } - It 'Should convert to word' { - (3).ToWords() | Should Be 'three' - } - It 'Should do math in weeks' { - (Get-Date 2/13/2016) + (3).Weeks -eq (Get-Date 3/5/2016) | Should Be $true - } - } - Context 'TimeSpan' { - $past = (Get-Date 2/13/2016).AddMinutes(-1).AddSeconds(-20) - $time = (Get-Date 2/13/2016) - $past - It 'Should simplify TimeSpan objects' { - $time.Humanize() | Should Be '1 minute' - } - It 'Should simplify TimeSpan objects with selectable precision' { - $time.Humanize(2) | Should Be '1 minute, 20 seconds' - } - } - Context 'DateTime' { - It 'Should display Now as now when UTC is false' { - (Get-Date).Humanize() | Should Be 'now' - } - It 'Should display Now as hours ago when UTC is true' { - (Get-Date).Humanize($true) | Should Match 'hours' - } - } -} - -Describe 'Custom Formats' { - Context 'TimeSpan' { - It 'Should display 1 hour' { - (([TimeSpan]::new(1,0,0))|Out-String).trim() | Should Be '1 hour' - } - } - Context 'FileSystem' { - $chars = [char[]] ([char]'0'..[char]'9' + [char]'A'..[char]'Z' + [char]'a'..[char]'z') - $chars = $chars * 126 - (1..(1kb/128)).foreach({-join (Get-Random $chars -Count 126) | Add-Content TestDrive:\testfile.txt }) - It 'Should display 1 KB' { - ((Get-ChildItem TestDrive:\testfile.txt) | Out-String) | Should Match "1 KB testfile.txt" - } - } -} - -Remove-Module PowerShellHumanizer \ No newline at end of file diff --git a/PowerShellHumanizer.psd1 b/PowerShellHumanizer.psd1 deleted file mode 100644 index 2184059..0000000 --- a/PowerShellHumanizer.psd1 +++ /dev/null @@ -1,126 +0,0 @@ -# -# Module manifest for module 'PowerShellHumanizer' -# -# Generated by: Douglas Finke -# -# Generated on: 5/7/2014 -# - -@{ - - # Script module or binary module file associated with this manifest. - RootModule = 'PowerShellHumanizer.psm1' - - # Version number of this module. - ModuleVersion = '3.2' - - # ID used to uniquely identify this module - GUID = '6dc9be51-eb93-4355-8648-0c725c0ac988' - - # Author of this module - Author = 'Douglas Finke and Chris Hunt' - - # Company or vendor of this module - CompanyName = 'No Company' - - # Copyright statement for this module - Copyright = 'c 2015 All rights reserved.' - - # Description of the functionality provided by this module - Description = 'PowerShell Humanizer wraps Humanizer: meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities' - - # Minimum version of the Windows PowerShell engine required by this module - # PowerShellVersion = '' - - # Name of the Windows PowerShell host required by this module - # PowerShellHostName = '' - - # Minimum version of the Windows PowerShell host required by this module - # PowerShellHostVersion = '' - - # Minimum version of Microsoft .NET Framework required by this module - # DotNetFrameworkVersion = '' - - # Minimum version of the common language runtime (CLR) required by this module - # CLRVersion = '' - - # Processor architecture (None, X86, Amd64) required by this module - # ProcessorArchitecture = '' - - # Modules that must be imported into the global environment prior to importing this module - # RequiredModules = @() - - # Assemblies that must be loaded prior to importing this module - # RequiredAssemblies = @() - - # Script files (.ps1) that are run in the caller's environment prior to importing this module. - # ScriptsToProcess = @() - - # Type files (.ps1xml) to be loaded when importing this module - #TypesToProcess = @('String.types.ps1xml', 'Int.types.ps1xml','TimeSpan.types.ps1xml','DateTime.types.ps1xml') - - # Format files (.ps1xml) to be loaded when importing this module - FormatsToProcess = @('TimeSpan.format.ps1xml') - - # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess - # NestedModules = @() - - # Functions to export from this module - FunctionsToExport = '*' - - # Cmdlets to export from this module - CmdletsToExport = '*' - - # Variables to export from this module - VariablesToExport = '*' - - # Aliases to export from this module - AliasesToExport = '*' - - # List of all modules packaged with this module - # ModuleList = @() - - # List of all files packaged with this module - # FileList = @() - - # Private data to pass to the module specified in RootModule/ModuleToProcess - PrivateData = @{ - # PSData is module packaging and gallery metadata embedded in PrivateData - # It's for rebuilding PowerShellGet (and PoshCode) NuGet-style packages - # We had to do this because it's the only place we're allowed to extend the manifest - # https://connect.microsoft.com/PowerShell/feedback/details/421837 - PSData = @{ - # The primary categorization of this module (from the TechNet Gallery tech tree). - Category = "PowerShell Humanizer" - - # Keyword tags to help users find this module via navigations and search. - Tags = @("PowerShell", "Humanizer") - - # The web address of an icon which can be used in galleries to represent this module - #IconUri = "http://pesterbdd.com/images/Pester.png" - - # The web address of this module's project or support homepage. - ProjectUri = "https://github.com/dfinke/PowerShellHumanizer" - - # The web address of this module's license. Points to a page that's embeddable and linkable. - LicenseUri = "https://github.com/dfinke/PowerShellHumanizer/blob/master/LICENSE.txt" - - # Release notes for this particular version of the module - # ReleaseNotes = False - - # If true, the LicenseUrl points to an end-user license (not just a source license) which requires the user agreement before use. - # RequireLicenseAcceptance = "" - - # Indicates this is a pre-release/testing version of the module. - IsPrerelease = 'False' - } - } - - # HelpInfo URI of this module - # HelpInfoURI = '' - - # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. - # DefaultCommandPrefix = '' - -} - diff --git a/PowerShellHumanizer.psm1 b/PowerShellHumanizer.psm1 deleted file mode 100644 index 3a7b0ac..0000000 --- a/PowerShellHumanizer.psm1 +++ /dev/null @@ -1,145 +0,0 @@ -Add-Type -Path "$PSScriptRoot\Humanizer.dll" - -$Types = @("$PSScriptRoot\String.types.ps1xml", - "$PSScriptRoot\Int.types.ps1xml", - "$PSScriptRoot\TimeSpan.types.ps1xml", - "$PSScriptRoot\DateTime.types.ps1xml", - "$PSScriptRoot\CmdletInfo.types.ps1xml") - -Update-TypeData -PrependPath $Types - -# Must call Update-FormatData with -PrependPath to override built-in defined formats -Update-FormatData -PrependPath "$PSScriptRoot\TimeSpan.format.ps1xml" -Update-FormatData -PrependPath "$PSScriptRoot\FileInfo.format.ps1xml" - -function ConvertFrom-RomanNumeral { - param( - [Parameter(ValueFromPipeline=$true)] - [string]$RomanNumeral - ) - - Process { - [Humanizer.RomanNumeralExtensions]::FromRoman($RomanNumeral) - } -} - -function ConvertTo-Casing { - param( - [Parameter(ValueFromPipeline=$true)] - [string]$Target, - [ValidateSet("Title","AllCaps","LowerCase","Sentence")] - $Case="Title" - ) - - Process { - [Humanizer.CasingExtensions]::ApplyCase($Target, $Case) - - } -} - -function ConvertTo-HumanDate { - param( - [Parameter(ValueFromPipeline=$true)] - [datetime]$Date=(Get-Date) - ) - - Process { - [Humanizer.DateHumanizeExtensions]::Humanize( $Date , $false ) - } -} - -function ConvertTo-Ordinal { - param( - [Parameter(ValueFromPipeline=$true)] - [int]$Target - ) - - Process { - [Humanizer.OrdinalizeExtensions]::Ordinalize($Target) - } -} - -function ConvertTo-OrdinalWords { - param( - [Parameter(ValueFromPipeline=$true)] - [int]$Target - ) - - Process { - [Humanizer.NumberToWordsExtension]::ToOrdinalWords($Target) - } -} - -function ConvertTo-Plural { - param( - [Parameter(ValueFromPipeline=$true)] - $Word - ) - - Process { - [Humanizer.InflectorExtensions]::Pluralize($word) - } -} - -function ConvertTo-Quantity { - param( - [string]$string, - - [Parameter(ValueFromPipeline=$true)] - [int]$quantity, - - [ValidateSet("None","Numeric","Words")] - $showQuantityAs="Numeric" - ) - - Process { - [Humanizer.ToQuantityExtensions]::ToQuantity($string, $quantity, $showQuantityAs) - } -} - - -function ConvertTo-RomanNumeral { - param( - [Parameter(ValueFromPipeline=$true)] - [int]$Number - ) - - Process { - [Humanizer.RomanNumeralExtensions]::ToRoman($Number) - } -} - -function ConvertTo-Singular { - param( - [Parameter(ValueFromPipeline=$true)] - $Word - ) - - Process { - [Humanizer.InflectorExtensions]::Singularize($word) - } -} - -function ConvertTo-Words { - param( - [Parameter(ValueFromPipeline=$true)] - [int]$number - ) - - Process { - [Humanizer.NumberToWordsExtension]::ToWords($number) - } -} - -function ConvertTo-HyphenatedString { - param( - [Parameter(ValueFromPipeline=$true)] - $TitleString - ) - - Process { - [Humanizer.InflectorExtensions]::Dasherize($TitleString.Underscore()) - } -} - - diff --git a/PublishToGallery.ps1 b/PublishToGallery.ps1 deleted file mode 100644 index 162d6d4..0000000 --- a/PublishToGallery.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -$p = @{ - Name = "PowerShellHumanizer" - NuGetApiKey = $NuGetApiKey - ReleaseNote = "Updated dependency, added Pester tests and more" -} - -Publish-Module @p \ No newline at end of file diff --git a/README.md b/README.md index 06588d7..ec1dee3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -

- -

# PowerShell Humanizer @@ -9,8 +6,11 @@ This PowerShell module wraps [Mehdi Khalili's .NET Humanizer](https://github.com > Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities [http://humanizr.net](http://humanizr.net) -Install -- +![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/cdhunt/PoshSSL/powershell.yml?style=flat&logo=github) +[![PowerShell Gallery](https://img.shields.io/powershellgallery/v/PowerShellHumanizer.svg?color=%235391FE&label=PowerShellGallery&logo=powershell&style=flat)](https://www.powershellgallery.com/packages/PowerShellHumanizer) + +## Install + To install grab it from the [Powershell Gallery](https://www.powershellgallery.com/packages/PowerShellHumanizer) ```powershell @@ -18,6 +18,13 @@ Install-Module -Name PowerShellHumanizer ``` ## What's new + +5/29/2024 + +No new functionality. +The Humanizer library has been updated to 3.0.0-beta13. +Documentation has been added for each command. + 4/24/2016 Check out how to call the `Humanize` capabilities. diff --git a/__tests__/PSHumanizer.tests.ps1 b/__tests__/PSHumanizer.tests.ps1 deleted file mode 100644 index 9bbaf21..0000000 --- a/__tests__/PSHumanizer.tests.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -Import-Module $PSScriptRoot\..\PowerShellHumanizer.psd1 -Force - -Describe "Test Humanizer" { - It "Flight test should be true" { - $true | Should Be $true - } -} \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 6a25765..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,20 +0,0 @@ -# https://developercommunity.visualstudio.com/content/problem/400181/pwsh-true-exports-but-fails-on-import.html -# resources: -# - repo: self -# queue: -# name: Hosted VS2017 - -jobs: -- job: Windows - pool: - vmImage: 'vs2017-win2016' - steps: - - powershell: .\DoTests.ps1 - displayName: 'Invoke Tests - Windows PowerShell' - - powershell: .\DoTests.ps1 -UsePSCore - displayName: 'Invoke Tests - PowerShell Core' - -trigger: - paths: - exclude: - - README.md \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..65cc242 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,245 @@ +#! /usr/bin/pwsh + +[CmdletBinding()] +param ( + [Parameter(Position = 0)] + [ValidateSet('clean', 'build', 'test', 'publish', 'docs')] + [string[]] + $Task, + + [Parameter(Position = 1)] + [int] + $Major, + + [Parameter(Position = 2)] + [int] + $Minor, + + [Parameter(Position = 3)] + [int] + $Build, + + [Parameter(Position = 4)] + [int] + $Revision, + + [Parameter(Position = 5)] + [string] + $Prerelease +) + +if ( (Get-Command 'nbgv' -CommandType Application -ErrorAction SilentlyContinue) ) { + if (!$PSBoundParameters.ContainsKey('Major')) { $Major = $(nbgv get-version -v VersionMajor) } + if (!$PSBoundParameters.ContainsKey('Minor')) { $Minor = $(nbgv get-version -v VersionMinor) } + if (!$PSBoundParameters.ContainsKey('Build')) { $Build = $(nbgv get-version -v BuildNumber) } + if (!$PSBoundParameters.ContainsKey('Revision')) { $Revision = $(nbgv get-version -v VersionRevision) } +} + +$module = 'PowerShellHumanizer' +$parent = $PSScriptRoot +$parent = if ([string]::IsNullOrEmpty($parent)) { $pwd.Path } else { $parent } +$src = Join-Path $parent -ChildPath "src" +$docs = Join-Path $parent -ChildPath "docs" +$publish = [System.IO.Path]::Combine($parent, "publish", $module) +$csproj = [System.IO.Path]::Combine($src, "dotnet", "dependencies.csproj") +$bin = [System.IO.Path]::Combine($src, "dotnet", "bin") +$obj = [System.IO.Path]::Combine($src, "dotnet", "obj") +$lib = [System.IO.Path]::Combine($publish, "lib") + +Write-Host "src: $src" +Write-Host "docs: $docs" +Write-Host "publish: $publish" +Write-Host "lib: $lib" +Write-Host "dotnet: $([Environment]::Version)" +Write-Host "ps: $($PSVersionTable.PSVersion)" + +$manifest = @{ + Path = Join-Path -Path $publish -ChildPath "$module.psd1" + Author = 'Douglas Finke and Chris Hunt' + CompanyName = 'No Company' + Copyright = 'c 2024 All rights reserved.' + CompatiblePSEditions = @("Desktop", "Core") + Description = 'PowerShell Humanizer wraps Humanizer: meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities' + GUID = '6dc9be51-eb93-4355-8648-0c725c0ac988' + + FunctionsToExport = @() + ModuleVersion = [version]::new($Major, $Minor, $Build, $Revision) + PowerShellVersion = '5.1' + ProjectUri = "https://github.com/dfinke/PowerShellHumanizer" + LicenseUri = "https://github.com/dfinke/PowerShellHumanizer/blob/master/LICENSE.txt" + RootModule = "$module.psm1" + Tags = @('PowerShell', 'Humanizer', 'strings', 'enums', 'dates', 'times', 'timespans', 'numbers', 'quantities') + + #RequiredModules = @( ) + #CmdletsToExport = '' + #VariablesToExport = '' + AliasesToExport = @('ConvertTo-DateWord','Kabab','Pluralize') + +} + +function Clean { + param () + + if (Test-Path $publish) { + Remove-Item -Path $publish -Recurse -Force + } +} + +function Dependencies { + param () + + Foreach ($mod in $manifest.RequiredModules) { + if ($null -eq (Get-Module -Name $mod.ModuleName -ListAvailable | Where-Object { [version]$_.Version -ge [version]$mod.ModuleVersion })) { + Install-Module $mod.ModuleName -RequiredVersion $mod.ModuleVersion -Scope CurrentUser -Confirm:$false -Force + } + } +} + +function Build { + param () + + New-Item -Path $publish -ItemType Directory -ErrorAction SilentlyContinue | Out-Null + + dotnet publish $csproj -o $lib + Get-ChildItem -Path $lib -filter "*.json" | Remove-Item -Force -ErrorAction SilentlyContinue + Get-ChildItem -Path $lib -filter "*.pdb" | Remove-Item -Force -ErrorAction SilentlyContinue + Get-ChildItem -Path $lib -filter "System.Management.Automation.dll" | Remove-Item -Force -ErrorAction SilentlyContinue + Get-ChildItem -Path $lib -filter "dependencies.dll" | Remove-Item -Force -ErrorAction SilentlyContinue + + Copy-Item -Path "$src/$module.psm1" -Destination $publish + Copy-Item -Path @("$parent/README.md") -Destination $publish -ErrorAction SilentlyContinue + + $publicFunctions = Get-ChildItem -Path "$src/public/*.ps1" + $privateFunctions = Get-ChildItem -Path "$src/private/*.ps1" -ErrorAction SilentlyContinue + $types = Get-ChildItem -Path "$src/types/*.ps1xml" + $formats = Get-ChildItem -Path "$src/formats/*.ps1xml" + + New-Item -Path "$publish/types" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null + foreach ($type in $types) { + Copy-Item -Path $type.FullName -Destination "$publish/types/$($type.Name)" + 'Update-TypeData -PrependPath "$PSSCriptRoot/types/{0}"' -f $type.Name | Add-Content "$publish/$module.psm1" + } + + New-Item -Path "$publish/formats" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null + foreach ($format in $formats) { + Copy-Item -Path $format.FullName -Destination "$publish/formats/$($format.Name)" + #'Update-FormatData -PrependPath "$PSSCriptRoot/formats/{0}"' -f $format.Name | Add-Content "$publish/$module.psm1" + } + + New-Item -Path "$publish/private" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null + foreach ($function in $privateFunctions) { + Copy-Item -Path $function.FullName -Destination "$publish/private/$($function.Name)" + '. "$PSSCriptRoot/private/{0}"' -f $function.Name | Add-Content "$publish/$module.psm1" + } + + New-Item -Path "$publish/public" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null + foreach ($function in $publicFunctions) { + Copy-Item -Path $function.FullName -Destination "$publish/public/$($function.Name)" + '. "$PSSCriptRoot/public/{0}"' -f $function.Name | Add-Content "$publish/$module.psm1" + $manifest.FunctionsToExport += $function.BaseName + } + + Copy-Item -Path "$src/en-US" -Destination "$publish" -Recurse + + if ($PSBoundParameters.ContainsKey('Prerelease')) { + $manifest.Add('Prerelease', $PreRelease) + } + + New-ModuleManifest @manifest + +} + +function Test { + param () + + if ($null -eq (Get-Module Pester -ListAvailable | Where-Object { [version]$_.Version -ge [version]"5.5.0" })) { + Install-Module -Name Pester -MinimumVersion 5.5.0 -Confirm:$false -Force + } + + $config = New-PesterConfiguration -Hashtable @{ + Run = @{ + Path = "tests" + Exit = if ($true -eq $env:CI) { $ture } else { $false } + } + TestResult = @{ + Enabled = $true + OutputFormat = "NUnitXml" + } + Output = @{ Verbosity = "Detailed" } + } + + Invoke-Pester -Configuration $config + +} + + +function Publish { + param () + + $repo = if ($env:PSPublishRepo) { $env:PSPublishRepo } else { 'PSGallery' } + + Publish-Module -Path $publish -Repository $repo -NuGetApiKey $env:PSPublishApiKey +} + +function Docs { + param () + + if ($null -eq (Get-Module Build-Docs -ListAvailable | Where-Object { [version]$_.Version -ge [version]"0.2.0" })) { + Install-Module -Name Build-Docs -MinimumVersion 0.2.0 -Confirm:$false -Force + } + + Import-Module Build-Docs + Import-Module $publish -Force + + $help = Get-HelpModuleData $module + $aliases = Get-Alias | Where-Object Source -eq $module + + # docs/README.md + $help | New-HelpDoc | + Add-ModuleProperty Name -H1 | + Add-ModuleProperty Description | + Add-HelpDocText "Commands" -H2 | + Add-ModuleCommand -AsLinks | + Add-HelpDocText "Aliases" -H2 | + Out-HelpDoc -Path 'docs/README.md' + + + $aliases | ForEach-Object { + '- `{0}` -> `{1}`' -f $_.Name, $_.Definition | Add-Content -Path 'docs/README.md' + } + + # Individual Commands + foreach ($command in $help.Commands) { + $name = $command.Name + $doc = New-HelpDoc -HelpModuleData $help + $doc.Text = $command.ToMD() + $doc | Out-HelpDoc -Path "docs/$name.md" + } +} + +switch ($Task) { + { $_ -contains 'clean' } { + Clean + } + { $_ -contains 'build' } { + Clean + Dependencies + Build + } + { $_ -contains 'test' } { + Dependencies + Test + } + { $_ -contains 'publish' } { + Dependencies + Publish + } + { $_ -contains 'docs' } { + Dependencies + Docs + } + Default { + Clean + Build + } +} \ No newline at end of file diff --git a/docs/ConvertFrom-RomanNumeral.md b/docs/ConvertFrom-RomanNumeral.md new file mode 100644 index 0000000..1a7e14c --- /dev/null +++ b/docs/ConvertFrom-RomanNumeral.md @@ -0,0 +1,24 @@ +# ConvertFrom-RomanNumeral + +Convert a Roman Numeral String to an Int. + +## Parameters + +### Parameter Set 1 + +- `[String[]]` **RomanNumeral** _One or more Roman Numeral Strings to convert._ Mandatory, ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +ConvertFrom-RomanNumeral IV +4 +``` + +## Outputs + +- `int` diff --git a/docs/ConvertTo-Casing.md b/docs/ConvertTo-Casing.md new file mode 100644 index 0000000..91a214d --- /dev/null +++ b/docs/ConvertTo-Casing.md @@ -0,0 +1,25 @@ +# ConvertTo-Casing + +Format the given string with the given case. Options are Title, AllCaps, LowerCase, and Sentence. + +## Parameters + +### Parameter Set 1 + +- `[String[]]` **Target** _One or more strings to format._ Mandatory, ValueFromPipeline +- `[Switch]` **Case** _The target casing. Options are Title, AllCaps, LowerCase, and Sentence. Default is Title._ + +## Examples + +### Example 1 + + + +```powershell +"the powershell gallery" | ConvertTo-Casing -Case Title +the Powershell Gallery +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-HumanDate.md b/docs/ConvertTo-HumanDate.md new file mode 100644 index 0000000..7a22d5d --- /dev/null +++ b/docs/ConvertTo-HumanDate.md @@ -0,0 +1,40 @@ +# ConvertTo-HumanDate + +Return a string representing time relative a given Date. + +## Parameters + +### Parameter Set 1 + +- `[DateTime]` **Date** _A DateTime object. Default is Now._ ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +ConvertTo-HumanDate +now +``` +### Example 2 + + + +```powershell +ConvertTo-HumanDate (Get-Date).AddDays(-1) +yesterday +``` +### Example 3 + + + +```powershell +ConvertTo-HumanDate (Get-Date).AddDays(-12) +12 days ago +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-HyphenatedString.md b/docs/ConvertTo-HyphenatedString.md new file mode 100644 index 0000000..52ce3a5 --- /dev/null +++ b/docs/ConvertTo-HyphenatedString.md @@ -0,0 +1,36 @@ +# ConvertTo-HyphenatedString + +Convert a string to a Hyphenated string. + +## Parameters + +### Parameter Set 1 + +- `[String]` **String** _A string to convert._ Mandatory, ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +ConvertTo-HyphenatedString "verb noun" +verb-noun +``` +### Example 2 + + + +```powershell +ConvertTo-HyphenatedString PowerShellHumanizer +power-shell-humanizer +``` + +## Notes + +This requires the input string to have some word sepration. Either whitespace or Title case. + +## Outputs + +- `string` diff --git a/docs/ConvertTo-Ordinal.md b/docs/ConvertTo-Ordinal.md new file mode 100644 index 0000000..38c4eda --- /dev/null +++ b/docs/ConvertTo-Ordinal.md @@ -0,0 +1,24 @@ +# ConvertTo-Ordinal + +Convert an int to an ordinal number with the correct suffix. + +## Parameters + +### Parameter Set 1 + +- `[Int32]` **Target** _An int to convert._ Mandatory, ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +ConvertTo-Ordinal 1 +1st +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-OrdinalWord.md b/docs/ConvertTo-OrdinalWord.md new file mode 100644 index 0000000..7871037 --- /dev/null +++ b/docs/ConvertTo-OrdinalWord.md @@ -0,0 +1,24 @@ +# ConvertTo-OrdinalWord + +Convert an int to an ordinal word. First, Second, etc. + +## Parameters + +### Parameter Set 1 + +- `[Int32]` **Target** _An int to convert._ ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +ConvertTo-OrdinalWord 2 +second +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-Plural.md b/docs/ConvertTo-Plural.md new file mode 100644 index 0000000..0b7ca09 --- /dev/null +++ b/docs/ConvertTo-Plural.md @@ -0,0 +1,24 @@ +# ConvertTo-Plural + +Convert the string to the pural form. + +## Parameters + +### Parameter Set 1 + +- `[Object]` **Word** _A string to convert._ Mandatory, ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +"dog" | ConvertTo-Plural +dogs +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-Quantity.md b/docs/ConvertTo-Quantity.md new file mode 100644 index 0000000..eab8708 --- /dev/null +++ b/docs/ConvertTo-Quantity.md @@ -0,0 +1,50 @@ +# ConvertTo-Quantity + +Return the correct plurality of a string for a given quantity. + +## Parameters + +### Parameter Set 1 + +- `[String]` **String** _A string to convert._ Mandatory, ValueFromPipeline +- `[Int32]` **Quantity** _A quantity of $String._ Mandatory +- `[Switch]` **ShowQuantityAs** _The format $Quantity should be displayed in. Options are None, Numeric, and Words. Default is Numeric._ + +## Examples + +### Example 1 + + + +```powershell +ConvertTo-Quantity -Quantity 2 -String "widget" -ShowQuantityAs "Words" +two widgets +``` +### Example 2 + + + +```powershell +ConvertTo-Quantity -Quantity 1 -String "widget" -ShowQuantityAs "Words" +one widget +``` +### Example 3 + + + +```powershell +ConvertTo-Quantity -Quantity 2 -String "widget" +2 widgets +``` +### Example 4 + + + +```powershell +ConvertTo-Quantity -Quantity 2 -String "widget" -ShowQuantityAs "None" +widgets +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-RomanNumeral.md b/docs/ConvertTo-RomanNumeral.md new file mode 100644 index 0000000..9f7bf34 --- /dev/null +++ b/docs/ConvertTo-RomanNumeral.md @@ -0,0 +1,24 @@ +# ConvertTo-RomanNumeral + +Convert an Int to a Roman Numeral String. + +## Parameters + +### Parameter Set 1 + +- `[Int32[]]` **Number** _One or more Ints to convert._ Mandatory, ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +ConvertTo-RomanNumeral 12 +XII +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-Singular.md b/docs/ConvertTo-Singular.md new file mode 100644 index 0000000..c108c3e --- /dev/null +++ b/docs/ConvertTo-Singular.md @@ -0,0 +1,24 @@ +# ConvertTo-Singular + +Convert the string to the singular form. + +## Parameters + +### Parameter Set 1 + +- `[String[]]` **Word** _One or more Strings to convert._ Mandatory, ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +"cats" | ConvertTo-Singular +cat +``` + +## Outputs + +- `string` diff --git a/docs/ConvertTo-Word.md b/docs/ConvertTo-Word.md new file mode 100644 index 0000000..cc82f4b --- /dev/null +++ b/docs/ConvertTo-Word.md @@ -0,0 +1,24 @@ +# ConvertTo-Word + +Convert an Int to a String of one or more words. + +## Parameters + +### Parameter Set 1 + +- `[Int32[]]` **Number** _One or more Ints to convert._ Mandatory, ValueFromPipeline + +## Examples + +### Example 1 + + + +```powershell +1337 | ConvertTo-Word +one thousand three hundred and thirty-seven +``` + +## Outputs + +- `string` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..332e70c --- /dev/null +++ b/docs/README.md @@ -0,0 +1,23 @@ +# PowerShellHumanizer + +PowerShell Humanizer wraps Humanizer: meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities + +## Commands + +- [ConvertFrom-RomanNumeral](ConvertFrom-RomanNumeral.md) _Convert a Roman Numeral String to an Int._ +- [ConvertTo-Casing](ConvertTo-Casing.md) _Format the given string with the given case._ +- [ConvertTo-HumanDate](ConvertTo-HumanDate.md) _Return a string representing time relative a given Date._ +- [ConvertTo-HyphenatedString](ConvertTo-HyphenatedString.md) _Convert a string to a Hyphenated string._ +- [ConvertTo-Ordinal](ConvertTo-Ordinal.md) _Convert an int to an ordinal number._ +- [ConvertTo-OrdinalWord](ConvertTo-OrdinalWord.md) _Convert an int to an ordinal word._ +- [ConvertTo-Plural](ConvertTo-Plural.md) _Convert the string to the pural form._ +- [ConvertTo-Quantity](ConvertTo-Quantity.md) _Return the correct plurality of a string for a given quantity._ +- [ConvertTo-RomanNumeral](ConvertTo-RomanNumeral.md) _Convert an Int to a Roman Numeral String._ +- [ConvertTo-Singular](ConvertTo-Singular.md) _Convert the string to the singular form._ +- [ConvertTo-Word](ConvertTo-Word.md) _Convert an Int to a String of one or more words._ +## Aliases + + +- `ConvertTo-DateWord` -> `ConvertTo-HumanDate` +- `Kabab` -> `ConvertTo-HyphenatedString` +- `Pluralize` -> `ConvertTo-Plural` diff --git a/docs/about_PowerShellHumanizer.md b/docs/about_PowerShellHumanizer.md new file mode 100644 index 0000000..eb6f857 --- /dev/null +++ b/docs/about_PowerShellHumanizer.md @@ -0,0 +1,51 @@ +# Topic + +about_PowerShellHumanizer + +## Short Description + +This PowerShell module wraps [Mehdi Khalili's .NET Humanizer](https://github.com/MehdiK/Humanizer). + +## Long Description + +Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities [http://humanizr.net](http://humanizr.net) + +## String Extension Methods + +- `.Humanize()` +- `.Humanize([Humanizer.LetterCasing]LetterCasing <"AllCaps","Sentence","Title","LowerCase">)` +- `.Transform([string]Case <"SentenceCase","TitleCase","LowerCase","UpperCase">)` +- `.ToSentenceCase()` +- `.ToTitleCase()` +- `.ToQuantity([int]Count)` +- `.ToQuantity([int]Count, [Humanizer.ShowQuantityAs]ShowQuantityAs <"Numeric","Word">)` +- `.Dehumanize()` +- `.Underscore()` +- `.FromRoman()` +- `.Truncate([int]Length)` +- `.Truncate([int]Length, [string]Truncator <"Characters","Words">)` +- `.Truncate([int]Length, [string]Truncator <"Characters","Words">, [string]TruncationString)` +- `.Truncate([int]Length, [string]Truncator <"Characters","Words">, [string]TruncationString, [Humanizer.TruncateFrom]From <"Left","Right">)` + +## Integer Extension Methods + +- `.Ordinalize()` +- `.ToWords()` +- `.ToRoman()` + +## Integer Extension Properties + +- `.Weeks` +- `.Days` +- `.Hours` +- `.Minutes` +- `.Seconds` +- `.Milliseconds` + +## Timespan Extension Methods + +- `.Humanize([int]Precision)` + +## Datetime Extension Methods + +- `.Humanize([bool]UTC)` diff --git a/DemoDateTime.ps1 b/example/DemoDateTime.ps1 similarity index 100% rename from DemoDateTime.ps1 rename to example/DemoDateTime.ps1 diff --git a/DemoFormats.ps1 b/example/DemoFormats.ps1 similarity index 100% rename from DemoFormats.ps1 rename to example/DemoFormats.ps1 diff --git a/DemoInts.ps1 b/example/DemoInts.ps1 similarity index 100% rename from DemoInts.ps1 rename to example/DemoInts.ps1 diff --git a/DemoStrings.ps1 b/example/DemoStrings.ps1 similarity index 100% rename from DemoStrings.ps1 rename to example/DemoStrings.ps1 diff --git a/src/PowerShellHumanizer.psm1 b/src/PowerShellHumanizer.psm1 new file mode 100644 index 0000000..8c870ac --- /dev/null +++ b/src/PowerShellHumanizer.psm1 @@ -0,0 +1,21 @@ +# Load dlls +if ($PSVersionTable.PSVersion -lt [version]"7.4.6") { + Add-Type -Path "$PSScriptRoot/lib/Humanizer.dll" +} + +if ($PSCulture -ne 'en-US') { + if (Test-Path "$PSScriptRoot/lib/$PSCulture") { + Add-Type -Path "$PSScriptRoot/lib/$PSCulture/Humanizer.resources.dll" + } else { + Write-Warning "Humanizer doesn't currently support '$PSCulture'." + } +} + +if (Get-Module -Name Terminal-Icons -ListAvailable -ErrorAction SilentlyContinue) { + Update-FormatData -PrependPath "$PSSCriptRoot/formats/FileInfoIcons.format.ps1xml" +} else { + Update-FormatData -PrependPath "$PSSCriptRoot/formats/FileInfo.format.ps1xml" +} + +Update-FormatData -PrependPath "$PSSCriptRoot/formats/TimeSpan.format.ps1xml" + diff --git a/src/dotnet/dependencies.csproj b/src/dotnet/dependencies.csproj new file mode 100644 index 0000000..d926ab6 --- /dev/null +++ b/src/dotnet/dependencies.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + 2.14.1 + + + + + + + diff --git a/en-US/about_PowerShellHumanizer.help.txt b/src/en-US/about_PowerShellHumanizer.help.txt similarity index 86% rename from en-US/about_PowerShellHumanizer.help.txt rename to src/en-US/about_PowerShellHumanizer.help.txt index cba572e..b71cea1 100644 --- a/en-US/about_PowerShellHumanizer.help.txt +++ b/src/en-US/about_PowerShellHumanizer.help.txt @@ -1,60 +1,60 @@ TOPIC - SampleModule 1.0.0 + about_PowerShellHumanizer SHORT DESCRIPTION This PowerShell module wraps [Mehdi Khalili's .NET Humanizer](https://github.com/MehdiK/Humanizer). LONG DESCRIPTION - Humanizer meets all your .NET needs for manipulating and displaying strings, enums, + Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities [http://humanizr.net](http://humanizr.net) STRING EXTENSION METHODS .Humanize() .Humanize([Humanizer.LetterCasing]LetterCasing <"AllCaps","Sentence","Title","LowerCase">) - + .Transform([string]Case <"SentenceCase","TitleCase","LowerCase","UpperCase">) - + .ToSentenceCase() - + .ToTitleCase() - + .ToQuantity([int]Count) .ToQuantity([int]Count, [Humanizer.ShowQuantityAs]ShowQuantityAs <"Numeric","Word">) - + .Dehumanize() - + .Underscore() - + .FromRoman() - + .Truncate([int]Length) .Truncate([int]Length, [string]Truncator <"Characters","Words">) .Truncate([int]Length, [string]Truncator <"Characters","Words">, [string]TruncationString) - .Truncate([int]Length, [string]Truncator <"Characters","Words">, [string]TruncationString, + .Truncate([int]Length, [string]Truncator <"Characters","Words">, [string]TruncationString, [Humanizer.TruncateFrom]From <"Left","Right">) INTEGER EXTENSION METHODS .Ordinalize() - + .ToWords() - + .ToRoman() - + INTEGER EXTENSION PROPERTIES .Weeks - + .Days - + .Hours - + .Minutes - + .Seconds - + .Milliseconds - + TIMESPAN EXTENSION METHODS .Humanize([int]Precision) - + DATETIME EXTENSION METHODS - .Humanize([bool]UTC) \ No newline at end of file + .Humanize([bool]UTC) \ No newline at end of file diff --git a/FileInfo.format.ps1xml b/src/formats/FileInfo.format.ps1xml similarity index 100% rename from FileInfo.format.ps1xml rename to src/formats/FileInfo.format.ps1xml diff --git a/src/formats/FileInfoIcons.format.ps1xml b/src/formats/FileInfoIcons.format.ps1xml new file mode 100644 index 0000000..48c60c3 --- /dev/null +++ b/src/formats/FileInfoIcons.format.ps1xml @@ -0,0 +1,231 @@ + + + + + + + FileSystemTypes + + System.IO.DirectoryInfo + System.IO.FileInfo + + + + + + + FileSystemTypes-GroupingFormat + + + + + + 4 + + + + + $_.PSParentPath.Replace("Microsoft.PowerShell.Core\FileSystem::", "") + + + + + + + + + + + + + + + children + + FileSystemTypes + + + PSParentPath + FileSystemTypes-GroupingFormat + + + + + + 7 + left + + + + 25 + right + + + + 14 + right + + + + + + + + + + + Mode + + + + [Humanizer.DateHumanizeExtensions]::Humanize($_.LastWriteTime,$false) + + + + + if (-not $_.PSIsContainer) { [Humanizer.ByteSizeExtensions]::bytes($_.Length).ToString(0.00)} + + + + + Terminal-Icons\Format-TerminalIcons $_ + + + + + + + + + children + + FileSystemTypes + + + PSParentPath + FileSystemTypes-GroupingFormat + + + + + + System.IO.FileInfo + + + + + + Terminal-Icons\Format-TerminalIcons $_ + + + + + if (-not $_.PSIsContainer) { [Humanizer.ByteSizeExtensions]::bytes($_.Length).ToString(0.00)} + + + + + [Humanizer.DateHumanizeExtensions]::Humanize($_.CreationTime,$false) + + + + + [Humanizer.DateHumanizeExtensions]::Humanize($_.LastWriteTime,$false) + + + + + [Humanizer.DateHumanizeExtensions]::Humanize($_.LastAccessTime,$false) + + + + Mode + + + LinkType + + + + + Terminal-Icons\Format-TerminalIcons $_ + + + + + + + + + + + Terminal-Icons\Format-TerminalIcons $_ + + + + + [Humanizer.DateHumanizeExtensions]::Humanize($_.CreationTime,$false) + + + + + [Humanizer.DateHumanizeExtensions]::Humanize($_.LastWriteTime,$false) + + + + + [Humanizer.DateHumanizeExtensions]::Humanize($_.LastAccessTime,$false) + + + + Mode + + + LinkType + + + + + Terminal-Icons\Format-TerminalIcons $_ + + + + + + + + + children + + FileSystemTypes + + + PSParentPath + FileSystemTypes-GroupingFormat + + + + + + + Terminal-Icons\Format-TerminalIcons $_ + + + + + + System.IO.DirectoryInfo + + + + Terminal-Icons\Format-TerminalIcons $_ + + + + + + + + \ No newline at end of file diff --git a/TimeSpan.format.ps1xml b/src/formats/TimeSpan.format.ps1xml similarity index 100% rename from TimeSpan.format.ps1xml rename to src/formats/TimeSpan.format.ps1xml diff --git a/src/public/ConvertFrom-RomanNumeral.ps1 b/src/public/ConvertFrom-RomanNumeral.ps1 new file mode 100644 index 0000000..1fa8960 --- /dev/null +++ b/src/public/ConvertFrom-RomanNumeral.ps1 @@ -0,0 +1,25 @@ +<# +.SYNOPSIS + Convert a Roman Numeral String to an Int. +.DESCRIPTION + Convert a Roman Numeral String to an Int. +.PARAMETER RomanNumeral + One or more Roman Numeral Strings to convert. +.EXAMPLE + ConvertFrom-RomanNumeral IV + 4 +#> +function ConvertFrom-RomanNumeral { + [CmdletBinding()] + [OutputType('int')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [string[]]$RomanNumeral + ) + + Process { + foreach ($rn in $RomanNumeral) { + [Humanizer.RomanNumeralExtensions]::FromRoman($rn) + } + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-Casing.ps1 b/src/public/ConvertTo-Casing.ps1 new file mode 100644 index 0000000..73c315d --- /dev/null +++ b/src/public/ConvertTo-Casing.ps1 @@ -0,0 +1,31 @@ +<# +.SYNOPSIS + Format the given string with the given case. +.DESCRIPTION + Format the given string with the given case. Options are Title, AllCaps, LowerCase, and Sentence. +.PARAMETER Target + One or more strings to format. +.PARAMETER Case + The target casing. Options are Title, AllCaps, LowerCase, and Sentence. Default is Title. +.EXAMPLE + "the powershell gallery" | ConvertTo-Casing -Case Title + the Powershell Gallery +#> +function ConvertTo-Casing { + [CmdletBinding()] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [string[]]$Target, + + [Parameter()] + [Humanizer.LetterCasing] + $Case = "Title" + ) + + Process { + foreach ($t in $Target) { + [Humanizer.CasingExtensions]::ApplyCase($t, $Case) + } + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-HumanDate.ps1 b/src/public/ConvertTo-HumanDate.ps1 new file mode 100644 index 0000000..0f134de --- /dev/null +++ b/src/public/ConvertTo-HumanDate.ps1 @@ -0,0 +1,30 @@ +<# +.SYNOPSIS + Return a string representing time relative a given Date. +.DESCRIPTION + Return a string representing time relative a given Date. +.PARAMETER Date + A DateTime object. Default is Now. +.EXAMPLE + ConvertTo-HumanDate + now +.EXAMPLE + ConvertTo-HumanDate (Get-Date).AddDays(-1) + yesterday +.EXAMPLE + ConvertTo-HumanDate (Get-Date).AddDays(-12) + 12 days ago +#> +function ConvertTo-HumanDate { + [CmdletBinding()] + [Alias('ConvertTo-DateWord')] + [OutputType('string')] + param( + [Parameter(Position = 0, ValueFromPipeline)] + [datetime]$Date = (Get-Date) + ) + + Process { + [Humanizer.DateHumanizeExtensions]::Humanize( $Date , $false ) + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-HyphenatedString.ps1 b/src/public/ConvertTo-HyphenatedString.ps1 new file mode 100644 index 0000000..fb9b499 --- /dev/null +++ b/src/public/ConvertTo-HyphenatedString.ps1 @@ -0,0 +1,30 @@ +<# +.SYNOPSIS + Convert a string to a Hyphenated string. +.DESCRIPTION + Convert a string to a Hyphenated string. +.PARAMETER String + A string to convert. +.NOTES + This requires the input string to have some word sepration. Either whitespace or Title case. +.EXAMPLE + ConvertTo-HyphenatedString "verb noun" + verb-noun +.EXAMPLE + ConvertTo-HyphenatedString PowerShellHumanizer + power-shell-humanizer +#> +function ConvertTo-HyphenatedString { + [CmdletBinding()] + [Alias('Kabab')] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [string] + $String + ) + + Process { + [Humanizer.InflectorExtensions]::Dasherize($String.Underscore()) + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-Ordinal.ps1 b/src/public/ConvertTo-Ordinal.ps1 new file mode 100644 index 0000000..7d79c64 --- /dev/null +++ b/src/public/ConvertTo-Ordinal.ps1 @@ -0,0 +1,23 @@ +<# +.SYNOPSIS + Convert an int to an ordinal number. +.DESCRIPTION + Convert an int to an ordinal number with the correct suffix. +.PARAMETER Target + An int to convert. +.EXAMPLE + ConvertTo-Ordinal 1 + 1st +#> +function ConvertTo-Ordinal { + [CmdletBinding()] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [int]$Target + ) + + Process { + [Humanizer.OrdinalizeExtensions]::Ordinalize($Target) + } +} diff --git a/src/public/ConvertTo-OrdinalWord.ps1 b/src/public/ConvertTo-OrdinalWord.ps1 new file mode 100644 index 0000000..b0a49d3 --- /dev/null +++ b/src/public/ConvertTo-OrdinalWord.ps1 @@ -0,0 +1,23 @@ +<# +.SYNOPSIS + Convert an int to an ordinal word. +.DESCRIPTION + Convert an int to an ordinal word. First, Second, etc. +.PARAMETER Target + An int to convert. +.EXAMPLE + ConvertTo-OrdinalWord 2 + second +#> +function ConvertTo-OrdinalWord { + [CmdletBinding()] + [OutputType('string')] + param( + [Parameter(ValueFromPipeline = $true)] + [int]$Target + ) + + Process { + [Humanizer.NumberToWordsExtension]::ToOrdinalWords($Target) + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-Plural.ps1 b/src/public/ConvertTo-Plural.ps1 new file mode 100644 index 0000000..8021088 --- /dev/null +++ b/src/public/ConvertTo-Plural.ps1 @@ -0,0 +1,25 @@ +<# +.SYNOPSIS + Convert the string to the pural form. +.DESCRIPTION + Convert the string to the pural form. +.PARAMETER Word + A string to convert. +.EXAMPLE + "dog" | ConvertTo-Plural + dogs +#> +function ConvertTo-Plural { + [CmdletBinding()] + [Alias('Pluralize')] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [Alias('String', 'Target')] + $Word + ) + + Process { + [Humanizer.InflectorExtensions]::Pluralize($word) + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-Quantity.ps1 b/src/public/ConvertTo-Quantity.ps1 new file mode 100644 index 0000000..fe0b724 --- /dev/null +++ b/src/public/ConvertTo-Quantity.ps1 @@ -0,0 +1,44 @@ +<# +.SYNOPSIS + Return the correct plurality of a string for a given quantity. +.DESCRIPTION + Return the correct plurality of a string for a given quantity. +.PARAMETER String + A string to convert. +.PARAMETER Quantity + A quantity of $String. +.PARAMETER ShowQuantityAs + The format $Quantity should be displayed in. Options are None, Numeric, and Words. Default is Numeric. +.EXAMPLE + ConvertTo-Quantity -Quantity 2 -String "widget" -ShowQuantityAs "Words" + two widgets +.EXAMPLE + ConvertTo-Quantity -Quantity 1 -String "widget" -ShowQuantityAs "Words" + one widget +.EXAMPLE + ConvertTo-Quantity -Quantity 2 -String "widget" + 2 widgets +.EXAMPLE + ConvertTo-Quantity -Quantity 2 -String "widget" -ShowQuantityAs "None" + widgets +#> +function ConvertTo-Quantity { + [CmdletBinding()] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [string]$String, + + [Parameter(Mandatory, Position = 1)] + [Alias('Count')] + [int]$Quantity, + + [Parameter(Position = 2)] + [Humanizer.ShowQuantityAs] + $ShowQuantityAs = [Humanizer.ShowQuantityAs]::Numeric + ) + + Process { + [Humanizer.ToQuantityExtensions]::ToQuantity($String, $Quantity, $ShowQuantityAs) + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-RomanNumeral.ps1 b/src/public/ConvertTo-RomanNumeral.ps1 new file mode 100644 index 0000000..4517aa9 --- /dev/null +++ b/src/public/ConvertTo-RomanNumeral.ps1 @@ -0,0 +1,25 @@ +<# +.SYNOPSIS + Convert an Int to a Roman Numeral String. +.DESCRIPTION + Convert an Int to a Roman Numeral String. +.PARAMETER Number + One or more Ints to convert. +.EXAMPLE + ConvertTo-RomanNumeral 12 + XII +#> +function ConvertTo-RomanNumeral { + [CmdletBinding()] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [int[]]$Number + ) + + Process { + foreach ($n in $Number) { + [Humanizer.RomanNumeralExtensions]::ToRoman($n) + } + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-Singular.ps1 b/src/public/ConvertTo-Singular.ps1 new file mode 100644 index 0000000..ed7a247 --- /dev/null +++ b/src/public/ConvertTo-Singular.ps1 @@ -0,0 +1,27 @@ +<# +.SYNOPSIS + Convert the string to the singular form. +.DESCRIPTION + Convert the string to the singular form. +.PARAMETER Word + One or more Strings to convert. +.EXAMPLE + "cats" | ConvertTo-Singular + cat +#> +function ConvertTo-Singular { + [CmdletBinding()] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [Alias('String', 'Target')] + [string[]] + $Word + ) + + Process { + foreach ($w in $Word) { + [Humanizer.InflectorExtensions]::Singularize($w) + } + } +} \ No newline at end of file diff --git a/src/public/ConvertTo-Word.ps1 b/src/public/ConvertTo-Word.ps1 new file mode 100644 index 0000000..24eee6f --- /dev/null +++ b/src/public/ConvertTo-Word.ps1 @@ -0,0 +1,26 @@ +<# +.SYNOPSIS + Convert an Int to a String of one or more words. +.DESCRIPTION + Convert an Int to a String of one or more words. +.PARAMETER Number + One or more Ints to convert. +.EXAMPLE + 1337 | ConvertTo-Word + one thousand three hundred and thirty-seven +#> +function ConvertTo-Word { + [CmdletBinding()] + [OutputType('string')] + param( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [int[]] + $Number + ) + + Process { + foreach ($n in $Number) { + [Humanizer.NumberToWordsExtension]::ToWords($n) + } + } +} \ No newline at end of file diff --git a/CmdletInfo.types.ps1xml b/src/types/CmdletInfo.types.ps1xml similarity index 100% rename from CmdletInfo.types.ps1xml rename to src/types/CmdletInfo.types.ps1xml diff --git a/DateTime.types.ps1xml b/src/types/DateTime.types.ps1xml similarity index 100% rename from DateTime.types.ps1xml rename to src/types/DateTime.types.ps1xml diff --git a/Int.types.ps1xml b/src/types/Int.types.ps1xml similarity index 100% rename from Int.types.ps1xml rename to src/types/Int.types.ps1xml diff --git a/String.types.ps1xml b/src/types/String.types.ps1xml similarity index 100% rename from String.types.ps1xml rename to src/types/String.types.ps1xml diff --git a/TimeSpan.types.ps1xml b/src/types/TimeSpan.types.ps1xml similarity index 100% rename from TimeSpan.types.ps1xml rename to src/types/TimeSpan.types.ps1xml diff --git a/tests/PowerShellHumanizer.Tests.ps1 b/tests/PowerShellHumanizer.Tests.ps1 new file mode 100644 index 0000000..33045ac --- /dev/null +++ b/tests/PowerShellHumanizer.Tests.ps1 @@ -0,0 +1,168 @@ + +Describe 'Functions' { + BeforeAll { + Import-Module "$PSScriptRoot/../publish/PowerShellHumanizer" -Force + } + + AfterAll { + Remove-Module PowerShellHumanizer + } + + Context 'Pluralize' { + It 'Should convert man to men' { + ConvertTo-Plural man | Should -Be 'men' + } + It 'Should convert an array' { + $output = Write-Output person man woman | ConvertTo-Plural + $output[0] | Should -Be 'people' + $output[1] | Should -Be 'men' + $output[2] | Should -Be 'women' + } + } + + Context 'Singularize' { + It 'Should convert to ' -ForEach @( + @{given = 'people'; expect = 'person' } + @{given = 'men'; expect = 'man' } + @{given = 'women'; expect = 'woman' } + @{given = 'geese'; expect = 'goose' } + @{given = 'indicies'; expect = 'indicy' } + @{given = 'oxen'; expect = 'ox' } + @{given = 'knives'; expect = 'knife' } + ) { + $result = $given | ConvertTo-Singular + $result | Should -Be $expect + } + + } + + Context 'Hyphenate' { + It 'Should convert to a hyphenated string' { + + "Continuing To Make Powershell A Bit More Human" | + ConvertTo-HyphenatedString | + Should -Be 'continuing-to-make-powershell-a-bit-more-human' + } + + } + + Context 'Number to ordinal words' { + It 'Should convert to words' { + ConvertTo-OrdinalWord 121 | Should -Be 'hundred and twenty-first' + } + It 'Should convert a range to words' { + $output = 120..122 | ConvertTo-OrdinalWord + $output[0] | Should -Be 'hundred and twentieth' + $output[1] | Should -Be 'hundred and twenty-first' + $output[2] | Should -Be 'hundred and twenty-second' + } + } + + Context 'Quantity' { + It 'Should return "" with -quantity -string ""' -ForEach @( + @{quant = 1; string = 'widgets'; expected = 'one widget' } + @{quant = 2; string = 'widgets'; expected = 'two widgets' } + ) { + $result = ConvertTo-Quantity -quantity $quant -string $string -showQuantityAs "Words" + $result | Should -Be $expected + } + } +} + +Describe 'Type Extension Methods' { + + Context 'Strings' { + + It 'Should transform to Upper Case' { + 'then add nodes under it.'.Transform("UpperCase") | Should -Be 'THEN ADD NODES UNDER IT.' + } + It 'Should convert to Title Case' { + 'then add nodes under it.'.ToTitleCase() | Should -Be 'Then Add Nodes Under It.' + } + It 'Should convert from Title Case' { + 'FromTitleCase'.Underscore() | Should -Be 'from_title_case' + } + It 'Should truncate words' { + 'then add nodes under it.'.Truncate(3, "Words") | Should -Match 'then add nodes\W$' + } + It 'Should truncate characters' { + 'then add nodes under it.'.Truncate(3, "Characters") | Should -Match 'th\W$' + } + It 'Should truncate with optional character' { + 'then add nodes under it.'.Truncate(7, "Characters", '-') | Should -Be 'then ad-' + } + It 'Should Dehumanize' { + 'then add nodes under it.'.Dehumanize() | Should -Be 'ThenAddNodesUnderIt' + } + It 'Should provide quanity: # word' { + 'string'.ToQuantity(50) | Should -Be '50 strings' + } + It 'Should provide quantity: words' { + 'string'.ToQuantity(50, "Words") | Should -Be 'fifty strings' + } + It 'Should convert Year to roman numerals' { + (Get-Date -Year 2024 ).Year.ToRoman() | Should -Be 'MMXXIV' + } + } + + Context 'Integers' { + It 'Should ordanalize' { + (3).Ordinalize() | Should -Be '3rd' + } + It 'Should convert to word' { + (3).ToWords() | Should -Be 'three' + } + It 'Should do math in weeks' { + (Get-Date 2/13/2016) + (3).Weeks -eq (Get-Date 3/5/2016) | Should -Be $true + } + } + + Context 'TimeSpan' { + BeforeAll { + $past = (Get-Date 2/13/2016).AddMinutes(-1).AddSeconds(-20) + $time = (Get-Date 2/13/2016) - $past + } + + It 'Should simplify TimeSpan objects' { + $time.Humanize() | Should -Be '1 minute' + } + It 'Should simplify TimeSpan objects with selectable precision' { + $time.Humanize(2) | Should -Be '1 minute, 20 seconds' + } + } + + Context 'DateTime' { + It 'Should display Now as now when UTC is false' { + (Get-Date).Humanize() | Should -Be 'now' + } + It 'Should display Now as hours ago when UTC is true' -Skip:($env:CI -eq 'True') { + (Get-Date).Humanize($true) | Should -Match 'hours' + } + } +} + +Describe 'Custom Formats' { + + Context 'TimeSpan' { + It 'Should display 1 hour' { + (([TimeSpan]::new(1, 0, 0)) | Out-String).trim() | Should -Be '1 hour' + } + } + + Context 'FileSystem' { + BeforeAll { + + $listAlphaLower = for ($i = 97; $i -le 122 ; $i++) { [char]$i } + $listAlphaUpper = for ($i = 65; $i -le 90 ; $i++) { [char]$i } + $listNumber = for ($i = 0; $i -le 9 ; $i++) { $i } + $charset = $listAlphaLower + $listAlphaUpper + $listNumber + + $content = (1..(1kb)).ForEach({ Get-Random $charset }) + -join $content | Set-Content TestDrive:\testfile.txt + $testData = Get-Item TestDrive:\testfile.txt | Out-String + } + It 'Should display 1 KB' { + $testData | Should -Match '1 KB .? {0,2}testfile\.txt' + } + } +} \ No newline at end of file diff --git a/version.json b/version.json new file mode 100644 index 0000000..8eb8b7c --- /dev/null +++ b/version.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", + "version": "4.0", + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} \ No newline at end of file