├── .gitattributes ├── .github └── workflows │ ├── CD.yml │ └── CI.yml ├── .gitignore ├── Changelog.md ├── Extensions.sln ├── License.txt ├── Readme.md ├── Tyrrrz.Extensions.Tests ├── AssemblyExtensionsTests.cs ├── CollectionExtensionsTests.cs ├── DictionaryExtensionsTests.cs ├── EncodingExtensionsTests.cs ├── EnumExtensionsTests.cs ├── EnumerableExtensionsTests.cs ├── MiscExtensionsTests.cs ├── NumericExtensionsTests.cs ├── SetExtensions.cs ├── StringExtensionsTests.cs ├── TaskExtensionsTests.cs ├── TestData │ ├── TestEnum.cs │ └── TestManifestResource.txt ├── Tyrrrz.Extensions.Tests.csproj ├── UriExtensionsTests.cs └── XmlExtensionsTests.cs └── Tyrrrz.Extensions ├── AssemblyExtensions.cs ├── CollectionExtensions.cs ├── DictionaryExtensions.cs ├── EncodingExtensions.cs ├── EnumExtensions.cs ├── EnumerableExtensions.cs ├── Internal └── RandomEx.cs ├── MiscExtensions.cs ├── NumericExtensions.cs ├── SetExtensions.cs ├── StringExtensions.cs ├── TaskExtensions.cs ├── Tyrrrz.Extensions.csproj ├── UriExtensions.cs └── XmlExtensions.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/CD.yml: -------------------------------------------------------------------------------- 1 | name: CD 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v1 15 | 16 | - name: Install .NET Core 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 3.0.100 20 | 21 | - name: Pack 22 | run: dotnet pack --configuration Release 23 | 24 | - name: Deploy 25 | run: dotnet nuget push Tyrrrz.Extensions/bin/Release/*.nupkg -s nuget.org -k ${{secrets.NUGET_TOKEN}} 26 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v1 12 | 13 | - name: Install .NET Core 14 | uses: actions/setup-dotnet@v1 15 | with: 16 | dotnet-version: 3.0.100 17 | 18 | - name: Build & test 19 | run: dotnet test --configuration Release 20 | 21 | - name: Coverage 22 | run: curl -s https://codecov.io/bash | bash -s -- -f Tyrrrz.Extensions.Tests/bin/Release/Coverage.xml -t ${{secrets.CODECOV_TOKEN}} -Z 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | ### v1.6.2 (19-Apr-2019) 2 | 3 | - Added `AddRange` extension for `ISet`. 4 | - Added ReSharper contract annotations that were removed by accident. 5 | 6 | ### v1.6.1 (11-Apr-2019) 7 | 8 | - Added `IsNullOrEmpty` and `IsNullOrWhiteSpace` extensions for `string`. 9 | - Removed `IsEmpty` and `IsWhiteSpace` extensions for `string`. 10 | - Added `IsNullOrEmpty` extension for `IEnumerable`. 11 | - Renamed `ExceptEmptyOrWhiteSpace` to `ExceptNullOrWhiteSpace`. -------------------------------------------------------------------------------- /Extensions.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.156 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2C661245-7C1D-4F45-AB03-2B257EDE9895}" 7 | ProjectSection(SolutionItems) = preProject 8 | Changelog.md = Changelog.md 9 | License.txt = License.txt 10 | Readme.md = Readme.md 11 | EndProjectSection 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tyrrrz.Extensions", "Tyrrrz.Extensions\Tyrrrz.Extensions.csproj", "{BC43C512-D60E-4E83-84F1-8CB41D7D65A4}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tyrrrz.Extensions.Tests", "Tyrrrz.Extensions.Tests\Tyrrrz.Extensions.Tests.csproj", "{1B08F6F8-B356-4901-909C-C186943B2D1A}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {BC43C512-D60E-4E83-84F1-8CB41D7D65A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {BC43C512-D60E-4E83-84F1-8CB41D7D65A4}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {BC43C512-D60E-4E83-84F1-8CB41D7D65A4}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {BC43C512-D60E-4E83-84F1-8CB41D7D65A4}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {1B08F6F8-B356-4901-909C-C186943B2D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {1B08F6F8-B356-4901-909C-C186943B2D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {1B08F6F8-B356-4901-909C-C186943B2D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {1B08F6F8-B356-4901-909C-C186943B2D1A}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | GlobalSection(ExtensibilityGlobals) = postSolution 36 | SolutionGuid = {5571B954-A4D0-407E-8F3B-4731DDE82390} 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2019 Alexey Golub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Tyrrrz.Extensions 2 | 3 | [![Build](https://github.com/Tyrrrz/Extensions/workflows/CI/badge.svg)](https://github.com/Tyrrrz/Extensions/actions) 4 | [![Coverage](https://codecov.io/gh/Tyrrrz/Extensions/branch/master/graph/badge.svg)](https://codecov.io/gh/Tyrrrz/Extensions) 5 | [![Version](https://img.shields.io/nuget/v/Tyrrrz.Extensions.svg)](https://nuget.org/packages/Tyrrrz.Extensions) 6 | [![Downloads](https://img.shields.io/nuget/dt/Tyrrrz.Extensions.svg)](https://nuget.org/packages/Tyrrrz.Extensions) 7 | [![Donate](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tyrrrz) 8 | [![Donate](https://img.shields.io/badge/buymeacoffee-donate-yellow.svg)](https://buymeacoffee.com/tyrrrz) 9 | 10 | This library contains all extension methods I use in my projects on a regular basis. You are welcome to use them too, either by copy-pasting them or by referencing this library. 11 | 12 | ## Download 13 | 14 | - [NuGet](https://nuget.org/packages/Tyrrrz.Extensions): `dotnet add package Tyrrrz.Extensions` 15 | 16 | ## Libraries used 17 | 18 | - [NUnit](https://github.com/nunit/nunit) 19 | - [Coverlet](https://github.com/tonerdo/coverlet) 20 | 21 | ## Donate 22 | 23 | If you really like my projects and want to support me, consider donating to me on [Patreon](https://patreon.com/tyrrrz) or [BuyMeACoffee](https://buymeacoffee.com/tyrrrz). All donations are optional and are greatly appreciated. 🙏 -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/AssemblyExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NUnit.Framework; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class AssemblyExtensionsTests 8 | { 9 | [Test] 10 | public void GetManifestResourceString_Test() 11 | { 12 | // Arrange 13 | var rootNamespace = typeof(AssemblyExtensionsTests).Namespace; 14 | var resourceName = $"{rootNamespace}.TestData.TestManifestResource.txt"; 15 | 16 | // Act 17 | var str = Assembly.GetExecutingAssembly().GetManifestResourceString(resourceName); 18 | 19 | // Assert 20 | Assert.That(str, Is.EqualTo("Hello world")); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/CollectionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class CollectionExtensionsTests 8 | { 9 | [Test] 10 | public void AddRange_Test() 11 | { 12 | // Arrange 13 | var collection = new List() as ICollection; 14 | var items = new[] 15 | { 16 | "hello", 17 | "world" 18 | }; 19 | 20 | // Act 21 | collection.AddRange(items); 22 | 23 | // Assert 24 | Assert.That(collection, Is.EqualTo(items)); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/DictionaryExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class DictionaryExtensionsTests 8 | { 9 | [Test] 10 | public void GetValueOrDefault_Test() 11 | { 12 | // Arrange 13 | var dic = new Dictionary 14 | { 15 | {"test", "1"}, 16 | {"aaa", "bbb"} 17 | }; 18 | 19 | // Act 20 | var existingValue = dic.GetValueOrDefault("test"); 21 | var nonExistingValue = dic.GetValueOrDefault("x"); 22 | 23 | // Assert 24 | Assert.That(existingValue, Is.EqualTo("1")); 25 | Assert.That(nonExistingValue, Is.EqualTo(default(string))); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/EncodingExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Tyrrrz.Extensions.Tests 4 | { 5 | [TestFixture] 6 | public class EncodingExtensionsTests 7 | { 8 | [Test] 9 | [TestCase("test")] 10 | [TestCase("")] 11 | public void GetBytes_GetString_Test(string input) 12 | { 13 | // Act 14 | var bytes = input.GetBytes(); 15 | var backToString = bytes.GetString(); 16 | 17 | // Assert 18 | Assert.That(backToString, Is.EqualTo(input)); 19 | } 20 | 21 | [Test] 22 | [TestCase(new byte[] {0, 13, 19, 22, 99})] 23 | [TestCase(new byte[0])] 24 | public void ToBase64_FromBase64_Test(byte[] input) 25 | { 26 | // Act 27 | var base64 = input.ToBase64(); 28 | var backToBytes = base64.FromBase64(); 29 | 30 | // Assert 31 | Assert.That(backToBytes, Is.EqualTo(input)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/EnumExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Tyrrrz.Extensions.Tests.TestData; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class EnumExtensionsTests 8 | { 9 | [Test] 10 | [TestCase("Two", TestEnum.Two)] 11 | [TestCase("tWo", TestEnum.Two, true)] 12 | public void ParseEnum_Test(string input, TestEnum expectedOutput, bool ignoreCase = true) 13 | { 14 | Assert.That(input.ParseEnum(ignoreCase), Is.EqualTo(expectedOutput)); 15 | } 16 | 17 | [Test] 18 | [TestCase("Two", TestEnum.Two)] 19 | [TestCase("tWo", TestEnum.Two, true)] 20 | [TestCase("Four", default(TestEnum))] 21 | [TestCase("", default(TestEnum))] 22 | [TestCase(null, default(TestEnum))] 23 | public void ParseEnumOrDefault_Test(string input, TestEnum expectedOutput, bool ignoreCase = true) 24 | { 25 | Assert.That(input.ParseEnumOrDefault(ignoreCase), Is.EqualTo(expectedOutput)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/EnumerableExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class EnumerableExtensionsTests 8 | { 9 | [Test] 10 | [TestCase(null, true)] 11 | [TestCase(new object[0], true)] 12 | [TestCase(new object[] {1, 2, 3}, false)] 13 | public void IsNullOrEmpty_Test(IEnumerable source, bool expectedOutput) 14 | { 15 | Assert.That(source.IsNullOrEmpty(), Is.EqualTo(expectedOutput)); 16 | } 17 | 18 | [Test] 19 | [TestCase(null, new object[0])] 20 | [TestCase(new object[0], new object[0])] 21 | [TestCase(new object[] {1, 2, 3}, new object[] {1, 2, 3})] 22 | public void EmptyIfNull_Test(IEnumerable source, IEnumerable expectedOutput) 23 | { 24 | Assert.That(source.EmptyIfNull(), Is.EqualTo(expectedOutput)); 25 | } 26 | 27 | [Test] 28 | public void GetSequenceHashCode_Test() 29 | { 30 | // Arrange 31 | var first = new[] {1, 2, 3}; 32 | var second = new[] {3, 2, 1}; 33 | var third = new[] {1, 2, 3}; 34 | 35 | // Act 36 | var firstHashCode = first.GetSequenceHashCode(); 37 | var secondHashCode = second.GetSequenceHashCode(); 38 | var thirdHashCode = third.GetSequenceHashCode(); 39 | var firstHashCodeIgnoreOrder = first.GetSequenceHashCode(true); 40 | var secondHashCodeIgnoreOrder = second.GetSequenceHashCode(true); 41 | var thirdHashCodeIgnoreOrder = third.GetSequenceHashCode(true); 42 | 43 | // Assert 44 | Assert.That(firstHashCode, Is.Not.EqualTo(secondHashCode)); 45 | Assert.That(firstHashCode, Is.EqualTo(thirdHashCode)); 46 | Assert.That(secondHashCode, Is.Not.EqualTo(thirdHashCode)); 47 | 48 | Assert.That(firstHashCodeIgnoreOrder, Is.EqualTo(secondHashCodeIgnoreOrder)); 49 | Assert.That(firstHashCodeIgnoreOrder, Is.EqualTo(thirdHashCodeIgnoreOrder)); 50 | Assert.That(secondHashCodeIgnoreOrder, Is.EqualTo(thirdHashCodeIgnoreOrder)); 51 | } 52 | 53 | [Test] 54 | public void Random_Test() 55 | { 56 | // Arrange 57 | var input = new[] {1, 2, 3}; 58 | 59 | // Act 60 | var random = input.Random(); 61 | 62 | // Assert 63 | Assert.That(input, Has.Some.EqualTo(random)); 64 | } 65 | 66 | [Test] 67 | public void RandomOrDefault_Test() 68 | { 69 | // Arrange 70 | var input1 = new[] {1, 2, 3}; 71 | var input2 = new int[0]; 72 | 73 | // Act 74 | var random1 = input1.RandomOrDefault(); 75 | var random2 = input2.RandomOrDefault(); 76 | 77 | // Assert 78 | Assert.That(input1, Has.Some.EqualTo(random1)); 79 | Assert.That(random2, Is.EqualTo(default(int))); 80 | } 81 | 82 | [Test] 83 | public void Distinct_Test() 84 | { 85 | // Arrange 86 | var input = new[] {100, 123, 150, 141, 193}; 87 | 88 | // Act 89 | var distinct = input.Distinct(i => i % 10); 90 | 91 | // Assert 92 | Assert.That(distinct, Is.EqualTo(new[] {100, 123, 141})); 93 | } 94 | 95 | [Test] 96 | [TestCase(new object[] {1, 1, 2, 2, 3, 3}, 2, new object[] {1, 1, 3, 3})] 97 | [TestCase(new object[] {1, 2, 3}, 5, new object[] {1, 2, 3})] 98 | public void Except_Test(IEnumerable source, object except, IEnumerable expectedOutput) 99 | { 100 | Assert.That(source.Except(except), Is.EqualTo(expectedOutput)); 101 | } 102 | 103 | [Test] 104 | [TestCase(new object?[] {null, 1, null, 2}, new object[] {1, 2})] 105 | [TestCase(new object[] {1, 2, 3}, new object[] {1, 2, 3})] 106 | public void ExceptDefault_Test(IEnumerable source, IEnumerable expectedOutput) 107 | { 108 | Assert.That(source.ExceptDefault(), Is.EqualTo(expectedOutput)); 109 | } 110 | 111 | [Test] 112 | [TestCase(new object[] {1, 2, 3, 4, 5}, 1, 2, new object[] {2, 3})] 113 | [TestCase(new object[] {1, 2, 3, 4, 5}, 0, 5, new object[] {1, 2, 3, 4, 5})] 114 | [TestCase(new object[] {1, 2, 3, 4, 5}, 2, 0, new object[0])] 115 | public void Slice_Test(IEnumerable source, int startAt, int count, IEnumerable expectedOutput) 116 | { 117 | Assert.That(source.Slice(startAt, count), Is.EqualTo(expectedOutput)); 118 | } 119 | 120 | [Test] 121 | [TestCase(new object[] {1, 2, 3, 4, 5}, 3, new object[] {3, 4, 5})] 122 | [TestCase(new object[] {1, 2, 3, 4, 5}, 5, new object[] {1, 2, 3, 4, 5})] 123 | [TestCase(new object[] {1, 2, 3, 4, 5}, 0, new object[0])] 124 | public void TakeLast_Test(IEnumerable source, int count, IEnumerable expectedOutput) 125 | { 126 | Assert.That(source.TakeLast(count), Is.EqualTo(expectedOutput)); 127 | } 128 | 129 | [Test] 130 | [TestCase(new object[] {1, 2, 3, 4, 5}, 3, new object[] {1, 2})] 131 | [TestCase(new object[] {1, 2, 3, 4, 5}, 5, new object[0])] 132 | [TestCase(new object[] {1, 2, 3, 4, 5}, 0, new object[] {1, 2, 3, 4, 5})] 133 | public void SkipLast_Test(IEnumerable source, int count, IEnumerable expectedOutput) 134 | { 135 | Assert.That(source.SkipLast(count), Is.EqualTo(expectedOutput)); 136 | } 137 | 138 | [Test] 139 | public void TakeLastWhile_Test() 140 | { 141 | // Arrange 142 | var input = new[] {6, 2, 10, 4, 5}; 143 | 144 | // Act 145 | var output = input.TakeLastWhile(i => i < 10); 146 | 147 | // Assert 148 | Assert.That(output, Is.EqualTo(new[] {4, 5})); 149 | } 150 | 151 | [Test] 152 | public void SkipLastWhile_Test() 153 | { 154 | // Arrange 155 | var input = new[] {6, 2, 10, 4, 5}; 156 | 157 | // Act 158 | var output = input.SkipLastWhile(i => i < 10); 159 | 160 | // Assert 161 | Assert.That(output, Is.EqualTo(new[] {6, 2, 10})); 162 | } 163 | 164 | [Test] 165 | [TestCase(new object[] {6, 2, 10, 4, 5, 10}, 10, 2)] 166 | [TestCase(new object[] {6, 2, 10, 4, 5}, 20, -1)] 167 | public void IndexOf_Test(IEnumerable source, object element, int expectedOutput) 168 | { 169 | Assert.That(source.IndexOf(element), Is.EqualTo(expectedOutput)); 170 | } 171 | 172 | [Test] 173 | [TestCase(new object[] {6, 2, 10, 4, 5, 10}, 10, 5)] 174 | [TestCase(new object[] {6, 2, 10, 4, 5}, 20, -1)] 175 | public void LastIndexOf_Test(IEnumerable source, object element, int expectedOutput) 176 | { 177 | Assert.That(source.LastIndexOf(element), Is.EqualTo(expectedOutput)); 178 | } 179 | 180 | [Test] 181 | public void GroupContiguous_Test() 182 | { 183 | // Arrange 184 | var input = new[] {1, 2, 3, 4, 5, 6, 7, 8}; 185 | 186 | // Act 187 | var groups = input.GroupContiguous(b => b.Count < 3); 188 | 189 | // Assert 190 | Assert.That(groups, Is.EqualTo(new[] {new[] {1, 2, 3}, new[] {4, 5, 6}, new[] {7, 8}})); 191 | } 192 | } 193 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/MiscExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class MiscExtensionsTests 8 | { 9 | [Test] 10 | [TestCase(1, new object[] {1, 2, 3, 4, 5}, true)] 11 | [TestCase(0, new object[] {1, 2, 3, 4, 5}, false)] 12 | [TestCase(1, new object[0], false)] 13 | [TestCase(1, new object[] {0, 0, 0, 1, 1}, true)] 14 | public void IsEither_Test(object input, IEnumerable variants, bool expectedOutput) 15 | { 16 | Assert.That(input.IsEither(variants), Is.EqualTo(expectedOutput)); 17 | } 18 | 19 | [Test] 20 | public void IsEither_Test() 21 | { 22 | Assert.That(5.IsEither(1, 2, 3, 4, 5)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/NumericExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Tyrrrz.Extensions.Tests 4 | { 5 | [TestFixture] 6 | public class NumericExtensionsTests 7 | { 8 | [Test] 9 | [TestCase(5, 0, 10, 5)] 10 | [TestCase(5, 5, 10, 5)] 11 | [TestCase(5, 0, 5, 5)] 12 | [TestCase(5, 0, 3, 3)] 13 | [TestCase(5, 8, 10, 8)] 14 | public void Clamp_Test(int input, int min, int max, int expectedOutput) 15 | { 16 | Assert.That(input.Clamp(min, max), Is.EqualTo(expectedOutput)); 17 | } 18 | 19 | [Test] 20 | [TestCase(5, 0, 5)] 21 | [TestCase(5, 5, 5)] 22 | [TestCase(5, 8, 8)] 23 | public void ClampMin_Test(int input, int min, int expectedOutput) 24 | { 25 | Assert.That(input.ClampMin(min), Is.EqualTo(expectedOutput)); 26 | } 27 | 28 | [Test] 29 | [TestCase(5, 10, 5)] 30 | [TestCase(5, 5, 5)] 31 | [TestCase(5, 3, 3)] 32 | public void ClampMax_Test(int input, int max, int expectedOutput) 33 | { 34 | Assert.That(input.ClampMax(max), Is.EqualTo(expectedOutput)); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/SetExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class SetExtensions 8 | { 9 | [Test] 10 | [TestCase(new object[] {1, 2, 3, 4, 5, 6}, new object[] {1, 2, 3, 4, 5, 6})] 11 | [TestCase(new object[] {1, 1, 2, 2, 3, 3}, new object[] {1, 2, 3})] 12 | public void ToHashSet_Test(IEnumerable source, IEnumerable expectedOutput) 13 | { 14 | Assert.That(source.ToHashSet(), Is.EqualTo(expectedOutput)); 15 | } 16 | 17 | [Test] 18 | [TestCase(new object[] {1, 2, 3}, new object[] {4, 5, 6}, new object[] {1, 2, 3, 4, 5, 6})] 19 | [TestCase(new object[] {1, 2, 3}, new object[] {2, 2, 6}, new object[] {1, 2, 3, 6})] 20 | public void AddRange_Test(IEnumerable source, IEnumerable items, IEnumerable expectedOutput) 21 | { 22 | // Arrange 23 | var set = source.ToHashSet(); 24 | var initialCount = set.Count; 25 | 26 | // Act 27 | var delta = set.AddRange(items); 28 | 29 | // Assert 30 | Assert.That(set, Is.EqualTo(expectedOutput)); 31 | Assert.That(delta, Is.EqualTo(set.Count - initialCount)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/StringExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | 5 | namespace Tyrrrz.Extensions.Tests 6 | { 7 | [TestFixture] 8 | public class StringExtensionsTests 9 | { 10 | [Test] 11 | [TestCase(null, true)] 12 | [TestCase("", true)] 13 | [TestCase(" ", false)] 14 | [TestCase("test", false)] 15 | [TestCase(" test", false)] 16 | public void IsNullOrEmpty_Test(string input, bool expectedOutput) 17 | { 18 | Assert.That(input.IsNullOrEmpty(), Is.EqualTo(expectedOutput)); 19 | } 20 | 21 | [Test] 22 | [TestCase(null, true)] 23 | [TestCase("", true)] 24 | [TestCase(" ", true)] 25 | [TestCase("test", false)] 26 | [TestCase(" test", false)] 27 | public void IsNullOrWhiteSpace_Test(string input, bool expectedOutput) 28 | { 29 | Assert.That(input.IsNullOrWhiteSpace(), Is.EqualTo(expectedOutput)); 30 | } 31 | 32 | [Test] 33 | [TestCase(null, "")] 34 | [TestCase("test", "test")] 35 | [TestCase(" ", " ")] 36 | public void EmptyIfNull_Test(string input, string expectedOutput) 37 | { 38 | Assert.That(input.EmptyIfNull(), Is.EqualTo(expectedOutput)); 39 | } 40 | 41 | [Test] 42 | [TestCase("123", true)] 43 | [TestCase("test", false)] 44 | [TestCase("123.5", false)] 45 | public void IsNumeric_Test(string input, bool expectedOutput) 46 | { 47 | Assert.That(input.IsNumeric(), Is.EqualTo(expectedOutput)); 48 | } 49 | 50 | [Test] 51 | [TestCase("test", true)] 52 | [TestCase("123", false)] 53 | [TestCase("test123", false)] 54 | public void IsAlphabetic_Test(string input, bool expectedOutput) 55 | { 56 | Assert.That(input.IsAlphabetic(), Is.EqualTo(expectedOutput)); 57 | } 58 | 59 | [Test] 60 | [TestCase("123", true)] 61 | [TestCase("test", true)] 62 | [TestCase("test123", true)] 63 | [TestCase("123.5", false)] 64 | public void IsAlphanumeric_Test(string input, bool expectedOutput) 65 | { 66 | Assert.That(input.IsAlphanumeric(), Is.EqualTo(expectedOutput)); 67 | } 68 | 69 | [Test] 70 | [TestCase("test", "te", "st")] 71 | [TestCase("test", "st", "test")] 72 | [TestCase("tetetetest", "te", "st")] 73 | [TestCase("tESt", "te", "St", StringComparison.OrdinalIgnoreCase)] 74 | public void TrimStart_Test(string input, string sub, string expectedOutput, 75 | StringComparison comparison = StringComparison.Ordinal) 76 | { 77 | Assert.That(input.TrimStart(sub, comparison), Is.EqualTo(expectedOutput)); 78 | } 79 | 80 | [Test] 81 | [TestCase("test", "st", "te")] 82 | [TestCase("test", "te", "test")] 83 | [TestCase("testststst", "st", "te")] 84 | [TestCase("tESt", "st", "tE", StringComparison.OrdinalIgnoreCase)] 85 | public void TrimEnd_Test(string input, string sub, string expectedOutput, 86 | StringComparison comparison = StringComparison.Ordinal) 87 | { 88 | Assert.That(input.TrimEnd(sub, comparison), Is.EqualTo(expectedOutput)); 89 | } 90 | 91 | [Test] 92 | [TestCase("test", "t", "es")] 93 | [TestCase("test", "es", "test")] 94 | [TestCase("tetesttete", "te", "st")] 95 | [TestCase("teTestTete", "te", "st", StringComparison.OrdinalIgnoreCase)] 96 | public void Trim_Test(string input, string sub, string expectedOutput, 97 | StringComparison comparison = StringComparison.Ordinal) 98 | { 99 | Assert.That(input.Trim(sub, comparison), Is.EqualTo(expectedOutput)); 100 | } 101 | 102 | [Test] 103 | [TestCase("test", "tset")] 104 | [TestCase("", "")] 105 | [TestCase("t", "t")] 106 | public void Reverse_Test(string input, string expectedOutput) 107 | { 108 | Assert.That(input.Reverse(), Is.EqualTo(expectedOutput)); 109 | } 110 | 111 | [Test] 112 | [TestCase("test", 3, "testtesttest")] 113 | [TestCase("test", 1, "test")] 114 | [TestCase("test", 0, "")] 115 | public void Repeat_Test(string input, int count, string expectedOutput) 116 | { 117 | Assert.That(input.Repeat(count), Is.EqualTo(expectedOutput)); 118 | } 119 | 120 | [Test] 121 | [TestCase('a', 3, "aaa")] 122 | [TestCase('a', 1, "a")] 123 | [TestCase('a', 0, "")] 124 | public void Repeat_Test(char input, int count, string expectedOutput) 125 | { 126 | Assert.That(input.Repeat(count), Is.EqualTo(expectedOutput)); 127 | } 128 | 129 | [Test] 130 | [TestCase("aaatestbbtest", "test", "xyz", "aaaxyzbbxyz")] 131 | [TestCase("aaatestbb", "test", "xyz", "aaaxyzbb")] 132 | [TestCase("aaabbc", "test", "xyz", "aaabbc")] 133 | [TestCase("aaaTESTbbTeStc", "test", "xyz", "aaaxyzbbxyzc", StringComparison.OrdinalIgnoreCase)] 134 | public void Replace_Test(string input, string oldValue, string newValue, string expectedOutput, 135 | StringComparison comparison = StringComparison.Ordinal) 136 | { 137 | Assert.That(input.Replace(oldValue, newValue, comparison), Is.EqualTo(expectedOutput)); 138 | } 139 | 140 | [Test] 141 | [TestCase("aaatestbbb", "test", "aaa")] 142 | [TestCase("aaatestbbbtestccc", "test", "aaa")] 143 | [TestCase("aaatestbbbtestccc", "xyz", "aaatestbbbtestccc")] 144 | [TestCase("testaaa", "test", "")] 145 | [TestCase("aaatEStbbb", "test", "aaa", StringComparison.OrdinalIgnoreCase)] 146 | public void SubstringUntil_Test(string input, string sub, string expectedOutput, 147 | StringComparison comparison = StringComparison.Ordinal) 148 | { 149 | Assert.That(input.SubstringUntil(sub, comparison), Is.EqualTo(expectedOutput)); 150 | } 151 | 152 | [Test] 153 | [TestCase("aaatestbbb", "test", "bbb")] 154 | [TestCase("aaatestbbbtestccc", "test", "bbbtestccc")] 155 | [TestCase("aaatestbbbtestccc", "xyz", "")] 156 | [TestCase("aaatest", "test", "")] 157 | [TestCase("aaatEStbbb", "test", "bbb", StringComparison.OrdinalIgnoreCase)] 158 | public void SubstringAfter_Test(string input, string sub, string expectedOutput, 159 | StringComparison comparison = StringComparison.Ordinal) 160 | { 161 | Assert.That(input.SubstringAfter(sub, comparison), Is.EqualTo(expectedOutput)); 162 | } 163 | 164 | [Test] 165 | [TestCase("aaatestbbb", "test", "aaa")] 166 | [TestCase("aaatestbbbtestccc", "test", "aaatestbbb")] 167 | [TestCase("aaatestbbbtestccc", "xyz", "aaatestbbbtestccc")] 168 | [TestCase("testaaa", "test", "")] 169 | [TestCase("aaatEStbbb", "test", "aaa", StringComparison.OrdinalIgnoreCase)] 170 | public void SubstringUntilLast_Test(string input, string sub, string expectedOutput, 171 | StringComparison comparison = StringComparison.Ordinal) 172 | { 173 | Assert.That(input.SubstringUntilLast(sub, comparison), Is.EqualTo(expectedOutput)); 174 | } 175 | 176 | [Test] 177 | [TestCase("aaatestbbb", "test", "bbb")] 178 | [TestCase("aaatestbbbtestccc", "test", "ccc")] 179 | [TestCase("aaatestbbbtestccc", "xyz", "")] 180 | [TestCase("aaatest", "test", "")] 181 | [TestCase("aaatEStbbb", "test", "bbb", StringComparison.OrdinalIgnoreCase)] 182 | public void SubstringAfterLast_Test(string input, string sub, string expectedOutput, 183 | StringComparison comparison = StringComparison.Ordinal) 184 | { 185 | Assert.That(input.SubstringAfterLast(sub, comparison), Is.EqualTo(expectedOutput)); 186 | } 187 | 188 | [Test] 189 | [TestCase(new[] {"aaa", "", " ", "bbb"}, new[] {"aaa", "bbb"})] 190 | public void ExceptNullOrWhiteSpace_Test(IEnumerable input, IEnumerable expectedOutput) 191 | { 192 | Assert.That(input.ExceptNullOrWhiteSpace(), Is.EqualTo(expectedOutput)); 193 | } 194 | 195 | [Test] 196 | [TestCase("testaaatestaaatestaaa", new[] {"aaa"}, new[] {"test", "test", "test"})] 197 | public void Split_Test(string input, string[] separators, string[] expectedOutput) 198 | { 199 | Assert.That(input.Split(separators), Is.EqualTo(expectedOutput)); 200 | } 201 | 202 | [Test] 203 | [TestCase("testaatestbbbtesta", new[] {'a', 'b'}, new[] {"test", "test", "test"})] 204 | public void Split_Test(string input, char[] separators, string[] expectedOutput) 205 | { 206 | // Using fully qualified name because it defaults to member method 207 | Assert.That(StringExtensions.Split(input, separators), Is.EqualTo(expectedOutput)); 208 | } 209 | 210 | [Test] 211 | [TestCase(new[] {"test", "test"}, ", ", "test, test")] 212 | public void JoinToString_Test(IEnumerable input, string separator, string expectedOutput) 213 | { 214 | Assert.That(input.JoinToString(separator), Is.EqualTo(expectedOutput)); 215 | } 216 | } 217 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/TaskExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using NUnit.Framework; 3 | 4 | namespace Tyrrrz.Extensions.Tests 5 | { 6 | [TestFixture] 7 | public class TaskExtensionsTests 8 | { 9 | [Test] 10 | public async Task ParallelSelectAsync_Test() 11 | { 12 | // Arrange 13 | var array = new[] {0, 1, 2, 3, 4}; 14 | 15 | // Act 16 | var selected = await array.ParallelSelectAsync(i => Task.Run(() => i * 2)); 17 | 18 | // Assert 19 | Assert.That(selected, Is.EqualTo(new[] {0, 2, 4, 6, 8})); 20 | } 21 | 22 | [Test] 23 | public async Task ParallelForEachAsync_Test() 24 | { 25 | // Arrange 26 | var array = new[] {0, 1, 2, 3, 4}; 27 | 28 | // Act 29 | await array.ParallelForEachAsync(i => Task.Run(() => array[i] = i * 2)); 30 | 31 | // Assert 32 | Assert.That(array, Is.EqualTo(new[] {0, 2, 4, 6, 8})); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/TestData/TestEnum.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrrrz.Extensions.Tests.TestData 2 | { 3 | public enum TestEnum { One, Two, Three } 4 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/TestData/TestManifestResource.txt: -------------------------------------------------------------------------------- 1 | Hello world -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/Tyrrrz.Extensions.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | false 6 | true 7 | true 8 | opencover 9 | bin/$(Configuration)/Coverage.xml 10 | enable 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/UriExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Tyrrrz.Extensions.Tests 4 | { 5 | [TestFixture] 6 | public class UriExtensionsTests 7 | { 8 | [Test] 9 | [TestCase("https://test.com", "https://test.com/")] 10 | [TestCase("www.test.com", "http://www.test.com/")] 11 | public void ToUri_Test(string input, string expectedOutput) 12 | { 13 | Assert.That(input.ToUri().ToString(), Is.EqualTo(expectedOutput)); 14 | } 15 | 16 | [Test] 17 | [TestCase("route/resource", "test.com", "http://test.com/route/resource")] 18 | [TestCase("/root", "https://test.com/other", "https://test.com/root")] 19 | public void ToUri_Test(string input, string baseUri, string expectedOutput) 20 | { 21 | Assert.That(input.ToUri(baseUri).ToString(), Is.EqualTo(expectedOutput)); 22 | } 23 | 24 | [Test] 25 | [TestCase("http://test.com", "a", "b", "http://test.com/?a=b")] 26 | [TestCase("http://test.com/?a=b", "a", "x", "http://test.com/?a=x")] 27 | [TestCase("http://test.com/?x=y&c=d", "a", "b", "http://test.com/?x=y&c=d&a=b")] 28 | public void SetQueryParameter_Test(string input, string key, string value, string expectedOutput) 29 | { 30 | Assert.That(input.ToUri().SetQueryParameter(key, value).ToString(), Is.EqualTo(expectedOutput)); 31 | } 32 | 33 | [Test] 34 | [TestCase("http://test.com", "a", "b", "http://test.com/a/b")] 35 | [TestCase("http://test.com/a/b", "a", "x", "http://test.com/a/x")] 36 | [TestCase("http://test.com/x/y/c/d", "a", "b", "http://test.com/x/y/c/d/a/b")] 37 | public void SetRouteParameter_Test(string input, string key, string value, string expectedOutput) 38 | { 39 | Assert.That(input.ToUri().SetRouteParameter(key, value).ToString(), Is.EqualTo(expectedOutput)); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions.Tests/XmlExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Xml.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace Tyrrrz.Extensions.Tests 6 | { 7 | [TestFixture] 8 | public class XmlExtensionsTests 9 | { 10 | [Test] 11 | public void StripNamespaces_Test() 12 | { 13 | // Arrange 14 | var ns = XNamespace.Get("http://test.name.space"); 15 | var xml = new XElement("root", 16 | new XElement("elem1", "content1", new XAttribute("attr1", "value1")), 17 | new XElement(ns + "elem2", "content2", new XAttribute(ns + "attr2", "value2")), 18 | new XElement("elem3", new XElement(ns + "content3_elem1"), new XElement("content3_elem2"))); 19 | 20 | // Act 21 | var strippedXml = xml.StripNamespaces(); 22 | 23 | // Assert 24 | Assert.That(strippedXml, Is.Not.SameAs(xml)); 25 | Assert.That(strippedXml.DescendantsAndSelf().Elements().Select(a => a.Name.NamespaceName), Has.All.Null.Or.Empty); 26 | Assert.That(strippedXml.DescendantsAndSelf().Attributes().Select(a => a.Name.NamespaceName), Has.All.Null.Or.Empty); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/AssemblyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Resources; 5 | 6 | namespace Tyrrrz.Extensions 7 | { 8 | /// 9 | /// Extensions for . 10 | /// 11 | public static class AssemblyExtensions 12 | { 13 | /// 14 | /// Reads the given manifest resource as a string. 15 | /// 16 | [return: NotNull] 17 | public static string GetManifestResourceString([NotNull] this Assembly assembly, [NotNull] string resourceName) 18 | { 19 | // Get manifest resource stream 20 | var stream = assembly.GetManifestResourceStream(resourceName); 21 | if (stream == null) 22 | throw new MissingManifestResourceException($"Resource [{resourceName}] doesn't exist."); 23 | 24 | // Read stream 25 | using (stream) 26 | using (var reader = new StreamReader(stream)) 27 | return reader.ReadToEnd(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Tyrrrz.Extensions 5 | { 6 | /// 7 | /// Extensions for . 8 | /// 9 | public static class CollectionExtensions 10 | { 11 | /// 12 | /// Adds multiple items to the collection. 13 | /// 14 | public static void AddRange([NotNull] this ICollection collection, [NotNull] IEnumerable items) 15 | { 16 | foreach (var item in items) 17 | collection.Add(item); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Tyrrrz.Extensions 5 | { 6 | /// 7 | /// Extensions for and . 8 | /// 9 | public static class DictionaryExtensions 10 | { 11 | #if !NETSTANDARD2_1 12 | /// 13 | /// Returns a value that corresponds to the given key or default if the key doesn't exist. 14 | /// 15 | public static TValue GetValueOrDefault([NotNull] this IDictionary dictionary, [NotNull] TKey key) 16 | { 17 | return dictionary.TryGetValue(key, out var result) ? result : default; 18 | } 19 | #endif 20 | } 21 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/EncodingExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Text; 4 | 5 | namespace Tyrrrz.Extensions 6 | { 7 | /// 8 | /// Extensions for . 9 | /// 10 | public static class EncodingExtensions 11 | { 12 | /// 13 | /// Converts a string to byte array. 14 | /// 15 | public static byte[] GetBytes([NotNull] this string s, [NotNull] Encoding encoding) 16 | { 17 | return encoding.GetBytes(s); 18 | } 19 | 20 | /// 21 | /// Converts a string to byte array using unicode encoding. 22 | /// 23 | public static byte[] GetBytes([NotNull] this string s) => s.GetBytes(Encoding.Unicode); 24 | 25 | /// 26 | /// Converts a byte array to string. 27 | /// 28 | public static string GetString([NotNull] this byte[] bytes, [NotNull] Encoding encoding) 29 | { 30 | return encoding.GetString(bytes, 0, bytes.Length); 31 | } 32 | 33 | /// 34 | /// Converts a byte array to string using unicode encoding. 35 | /// 36 | public static string GetString([NotNull] this byte[] bytes) => bytes.GetString(Encoding.Unicode); 37 | 38 | /// 39 | /// Converts a byte array to a base64 string. 40 | /// 41 | public static string ToBase64([NotNull] this byte[] bytes) 42 | { 43 | return Convert.ToBase64String(bytes); 44 | } 45 | 46 | /// 47 | /// Converts a base64 string to a byte array. 48 | /// 49 | public static byte[] FromBase64([NotNull] this string s) 50 | { 51 | return Convert.FromBase64String(s); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Tyrrrz.Extensions 5 | { 6 | /// 7 | /// Extensions for . 8 | /// 9 | public static class EnumExtensions 10 | { 11 | /// 12 | /// Parses an enum value of a given type from a string. 13 | /// 14 | public static TEnum ParseEnum([NotNull] this string value, bool ignoreCase = true) where TEnum : struct, Enum 15 | { 16 | return (TEnum) Enum.Parse(typeof(TEnum), value, ignoreCase); 17 | } 18 | 19 | /// 20 | /// Parses an enum value of a given type from a string or returns default value if unsuccessful. 21 | /// 22 | public static TEnum ParseEnumOrDefault([MaybeNull] this string str, bool ignoreCase = true) where TEnum : struct, Enum 23 | { 24 | return Enum.TryParse(str, ignoreCase, out TEnum result) ? result : default; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using Tyrrrz.Extensions.Internal; 6 | 7 | namespace Tyrrrz.Extensions 8 | { 9 | /// 10 | /// Extensions for . 11 | /// 12 | public static class EnumerableExtensions 13 | { 14 | /// 15 | /// Indicates whether the sequence is null or an empty sequence. 16 | /// 17 | public static bool IsNullOrEmpty([NotNullWhen(false), MaybeNull] this IEnumerable source) => source == null || !source.Any(); 18 | 19 | /// 20 | /// Returns an empty sequence if the given sequence is null, otherwise returns given sequence. 21 | /// 22 | [return: NotNull] 23 | public static IEnumerable EmptyIfNull([MaybeNull] this IEnumerable source) => source ?? Enumerable.Empty(); 24 | 25 | /// 26 | /// Calculates aggregated hash code of all elements in a sequence. 27 | /// 28 | public static int GetSequenceHashCode([NotNull] this IEnumerable source, bool ignoreOrder = false) 29 | { 30 | // Calculate all hashes 31 | var hashes = source.Select(i => i?.GetHashCode() ?? 0); 32 | 33 | // If the order is irrelevant - order the list to ensure the order is always the same 34 | if (ignoreOrder) 35 | hashes = hashes.OrderBy(i => i); 36 | 37 | // Aggregate individual hashes 38 | var result = 19; 39 | foreach (var hash in hashes) 40 | { 41 | unchecked 42 | { 43 | result = result * 31 + hash; 44 | } 45 | } 46 | 47 | return result; 48 | } 49 | 50 | /// 51 | /// Returns a random element in a sequence. 52 | /// 53 | [return: NotNull] 54 | public static T Random([NotNull] this IEnumerable source) 55 | { 56 | // Buffer all elements 57 | var asReadOnlyList = source as IReadOnlyList ?? source.ToArray(); 58 | 59 | // If there are no elements - throw 60 | if (!asReadOnlyList.Any()) 61 | throw new InvalidOperationException("Sequence contains no elements."); 62 | 63 | return asReadOnlyList[RandomEx.GetInt(0, asReadOnlyList.Count)]; 64 | } 65 | 66 | /// 67 | /// Returns a random element in a sequence or default if there are no elements. 68 | /// 69 | [return: MaybeNull] 70 | public static T RandomOrDefault([NotNull] this IEnumerable source) 71 | { 72 | // Buffer all elements 73 | var asReadOnlyList = source as IReadOnlyList ?? source.ToArray(); 74 | 75 | // If there are no elements - return default 76 | if (!asReadOnlyList.Any()) 77 | return default!; 78 | 79 | return asReadOnlyList[RandomEx.GetInt(0, asReadOnlyList.Count)]; 80 | } 81 | 82 | /// 83 | /// Returns elements with distinct keys. 84 | /// 85 | [return: NotNull] 86 | public static IEnumerable Distinct([NotNull] this IEnumerable source, 87 | [NotNull] Func keySelector, [NotNull] IEqualityComparer keyComparer) 88 | { 89 | // Use a hashset to maintain uniqueness of keys 90 | var keyHashSet = new HashSet(keyComparer); 91 | foreach (var element in source) 92 | { 93 | if (keyHashSet.Add(keySelector(element))) 94 | yield return element; 95 | } 96 | } 97 | 98 | /// 99 | /// Returns elements with distinct keys. 100 | /// 101 | [return: NotNull] 102 | public static IEnumerable Distinct([NotNull] this IEnumerable source, [NotNull] Func keySelector) => 103 | source.Distinct(keySelector, EqualityComparer.Default); 104 | 105 | /// 106 | /// Discards elements from a sequence that are equal to given value. 107 | /// 108 | [return: NotNull] 109 | public static IEnumerable Except([NotNull] this IEnumerable source, T value, 110 | [NotNull] IEqualityComparer comparer) 111 | { 112 | return source.Where(i => !comparer.Equals(i, value)); 113 | } 114 | 115 | /// 116 | /// Discards elements from a sequence that are equal to given value. 117 | /// 118 | [return: NotNull] 119 | public static IEnumerable Except([NotNull] this IEnumerable source, T value) => source.Except(value, EqualityComparer.Default); 120 | 121 | /// 122 | /// Discards default values from a sequence. 123 | /// 124 | [return: NotNull] 125 | public static IEnumerable ExceptDefault([NotNull] this IEnumerable source) => source.Except(default!); 126 | 127 | /// 128 | /// Slices a sequence into a subsequence. 129 | /// 130 | [return: NotNull] 131 | public static IEnumerable Slice([NotNull] this IEnumerable source, int startAt, int count) 132 | { 133 | // If count is zero - return empty 134 | if (count == 0) 135 | yield break; 136 | 137 | var i = 0; 138 | foreach (var element in source) 139 | { 140 | // If the index is within range - yield element 141 | if (i >= startAt && i <= startAt + count - 1) 142 | yield return element; 143 | 144 | // If the index is past bounds - break 145 | if (i >= startAt + count) 146 | yield break; 147 | 148 | // Increment index 149 | i++; 150 | } 151 | } 152 | 153 | /// 154 | /// Returns a specified number of contiguous elements at the end of a sequence. 155 | /// 156 | [return: NotNull] 157 | public static IEnumerable TakeLast([NotNull] this IEnumerable source, int count) 158 | { 159 | // If count is 0 - return empty 160 | if (count == 0) 161 | return Enumerable.Empty(); 162 | 163 | // Buffer all elements 164 | var asReadOnlyList = source as IReadOnlyList ?? source.ToArray(); 165 | 166 | // If count is greater than element count - return source 167 | if (count >= asReadOnlyList.Count) 168 | return asReadOnlyList; 169 | 170 | // Otherwise - slice 171 | return asReadOnlyList.Slice(asReadOnlyList.Count - count, count); 172 | } 173 | 174 | /// 175 | /// Bypasses a specified number of contiguous elements at the end of a sequence. 176 | /// 177 | [return: NotNull] 178 | public static IEnumerable SkipLast([NotNull] this IEnumerable source, int count) 179 | { 180 | // If count is 0 - return source 181 | if (count == 0) 182 | return source; 183 | 184 | // Buffer all elements 185 | var asReadOnlyList = source as IReadOnlyList ?? source.ToArray(); 186 | 187 | // If count is greater than element count - return empty 188 | if (count >= asReadOnlyList.Count) 189 | return Enumerable.Empty(); 190 | 191 | // Otherwise - slice 192 | return asReadOnlyList.Slice(0, asReadOnlyList.Count - count); 193 | } 194 | 195 | /// 196 | /// Returns elements from the end of a sequence as long as a specified condition is true. 197 | /// 198 | [return: NotNull] 199 | public static IEnumerable TakeLastWhile([NotNull] this IEnumerable source, 200 | [NotNull] Func predicate) 201 | { 202 | return source.Reverse().TakeWhile(predicate).Reverse(); 203 | } 204 | 205 | /// 206 | /// Bypasses elements from the end of a sequence as long as a specified condition is true. 207 | /// 208 | [return: NotNull] 209 | public static IEnumerable SkipLastWhile([NotNull] this IEnumerable source, 210 | [NotNull] Func predicate) 211 | { 212 | return source.Reverse().SkipWhile(predicate).Reverse(); 213 | } 214 | 215 | /// 216 | /// Returns index of the first element in a sequence that matches the predicate. 217 | /// If there is no such element - returns -1; 218 | /// 219 | public static int IndexOf([NotNull] this IEnumerable source, [NotNull] Func predicate) 220 | { 221 | var i = 0; 222 | foreach (var element in source) 223 | { 224 | // If matches - return index 225 | if (predicate(element)) 226 | return i; 227 | 228 | // Increment index 229 | i++; 230 | } 231 | 232 | // If nothing found - return -1 233 | return -1; 234 | } 235 | 236 | /// 237 | /// Returns index of the first element in a sequence equal to given value. 238 | /// If there is no such element - returns -1; 239 | /// 240 | public static int IndexOf([NotNull] this IEnumerable source, T element, 241 | [NotNull] IEqualityComparer comparer) 242 | { 243 | return source.IndexOf(i => comparer.Equals(i, element)); 244 | } 245 | 246 | /// 247 | /// Returns index of the first element in a sequence equal to given value. 248 | /// If there is no such element - returns -1; 249 | /// 250 | public static int IndexOf([NotNull] this IEnumerable source, T element) => 251 | source.IndexOf(element, EqualityComparer.Default); 252 | 253 | /// 254 | /// Returns index of the last element in a sequence that matches the predicate. 255 | /// If there is no such element - returns -1; 256 | /// 257 | public static int LastIndexOf([NotNull] this IEnumerable source, [NotNull] Func predicate) 258 | { 259 | // Buffer all elements 260 | var asReadOnlyList = source as IReadOnlyList ?? source.ToArray(); 261 | 262 | // Loop in reverse 263 | for (var i = asReadOnlyList.Count - 1; i >= 0; i--) 264 | { 265 | // If matches - return index 266 | if (predicate(asReadOnlyList[i])) 267 | return i; 268 | } 269 | 270 | // If nothing found - return -1 271 | return -1; 272 | } 273 | 274 | /// 275 | /// Returns index of the last element in a sequence equal to given value. 276 | /// If there is no such element - returns -1; 277 | /// 278 | public static int LastIndexOf([NotNull] this IEnumerable source, T element, 279 | [NotNull] IEqualityComparer comparer) 280 | { 281 | return source.LastIndexOf(i => comparer.Equals(i, element)); 282 | } 283 | 284 | /// 285 | /// Returns index of the last element in a sequence equal to given value. 286 | /// If there is no such element - returns -1; 287 | /// 288 | public static int LastIndexOf([NotNull] this IEnumerable source, T element) => 289 | source.LastIndexOf(element, EqualityComparer.Default); 290 | 291 | /// 292 | /// Groups contiguous elements into a list based on a predicate. 293 | /// The predicate decides whether the next element should be added to the current group. 294 | /// If the predicate fails, the current group is closed and a new one, containing this element, is created. 295 | /// 296 | [return: NotNull] 297 | public static IEnumerable> GroupContiguous([NotNull] this IEnumerable source, 298 | [NotNull] Func, T, bool> groupPredicate) 299 | { 300 | // Create buffer 301 | var buffer = new List(); 302 | 303 | // Enumerate source 304 | foreach (var element in source) 305 | { 306 | // If buffer is not empty and group predicate failed - yield and reset buffer 307 | if (buffer.Any() && !groupPredicate(buffer, element)) 308 | { 309 | yield return buffer; 310 | buffer = new List(); // new instance to reset reference 311 | } 312 | 313 | // Add element to buffer 314 | buffer.Add(element); 315 | } 316 | 317 | // If buffer still has something after the source has been enumerated - yield 318 | if (buffer.Any()) 319 | yield return buffer; 320 | } 321 | 322 | /// 323 | /// Groups contiguous elements into a list based on a predicate. 324 | /// The predicate decides whether the next element should be added to the current group. 325 | /// If the predicate fails, the current group is closed and a new one, containing this element, is created. 326 | /// 327 | [return: NotNull] 328 | public static IEnumerable> GroupContiguous([NotNull] this IEnumerable source, 329 | [NotNull] Func, bool> groupPredicate) 330 | { 331 | return source.GroupContiguous((buffer, _) => groupPredicate(buffer)); 332 | } 333 | } 334 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/Internal/RandomEx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tyrrrz.Extensions.Internal 4 | { 5 | internal static class RandomEx 6 | { 7 | private static readonly Random _random = new Random(); 8 | 9 | public static int GetInt(int inclusiveMin = 0, int exclusiveMax = int.MaxValue) 10 | { 11 | lock (_random) 12 | { 13 | return _random.Next(inclusiveMin, exclusiveMax); 14 | } 15 | } 16 | 17 | public static double GetDouble() 18 | { 19 | lock (_random) 20 | { 21 | return _random.NextDouble(); 22 | } 23 | } 24 | 25 | public static void FillBytes(byte[] output) 26 | { 27 | lock (_random) 28 | { 29 | _random.NextBytes(output); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/MiscExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Linq; 4 | 5 | namespace Tyrrrz.Extensions 6 | { 7 | /// 8 | /// Misc extensions. 9 | /// 10 | public static class MiscExtensions 11 | { 12 | /// 13 | /// Determines whether an object is equal to any of the elements in a sequence. 14 | /// 15 | public static bool IsEither(this T obj, [NotNull] IEnumerable variants, 16 | [NotNull] IEqualityComparer comparer) 17 | { 18 | return variants.Contains(obj, comparer); 19 | } 20 | 21 | /// 22 | /// Determines whether an object is equal to any of the elements in a sequence. 23 | /// 24 | public static bool IsEither(this T obj, [NotNull] IEnumerable variants) => 25 | IsEither(obj, variants, EqualityComparer.Default); 26 | 27 | /// 28 | /// Determines whether the object is equal to any of the parameters. 29 | /// 30 | public static bool IsEither(this T obj, params T[] variants) => IsEither(obj, (IEnumerable) variants); 31 | } 32 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/NumericExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Tyrrrz.Extensions 5 | { 6 | /// 7 | /// Extensions for numeric types. 8 | /// 9 | public static class NumericExtensions 10 | { 11 | /// 12 | /// Clamps a value to a given range. 13 | /// 14 | [return: NotNull] 15 | public static T Clamp([NotNull] this T value, [NotNull] T min, [NotNull] T max) where T : IComparable 16 | { 17 | // Ensure max is greater than min 18 | if (max.CompareTo(min) < 0) 19 | throw new ArgumentException("Max must be greater than or equal to min."); 20 | 21 | // If value is less than min - return min 22 | if (value.CompareTo(min) <= 0) 23 | return min; 24 | 25 | // If value is greater than max - return max 26 | if (value.CompareTo(max) >= 0) 27 | return max; 28 | 29 | // Otherwise - return value 30 | return value; 31 | } 32 | 33 | /// 34 | /// Clamps a value to a given range. 35 | /// 36 | [return: NotNull] 37 | public static T ClampMin([NotNull] this T value, [NotNull] T min) where T : IComparable 38 | { 39 | // If value is less than min - return min 40 | if (value.CompareTo(min) <= 0) 41 | return min; 42 | 43 | // Otherwise - return value 44 | return value; 45 | } 46 | 47 | /// 48 | /// Clamps a value to a given range. 49 | /// 50 | [return: NotNull] 51 | public static T ClampMax([NotNull] this T value, [NotNull] T max) where T : IComparable 52 | { 53 | // If value is greater than max - return max 54 | if (value.CompareTo(max) >= 0) 55 | return max; 56 | 57 | // Otherwise - return value 58 | return value; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/SetExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Linq; 4 | 5 | namespace Tyrrrz.Extensions 6 | { 7 | /// 8 | /// Extensions for . 9 | /// 10 | public static class SetExtensions 11 | { 12 | /// 13 | /// Creates a hashset from given sequence. 14 | /// 15 | [return: NotNull] 16 | public static HashSet ToHashSet([NotNull] this IEnumerable source, [NotNull] IEqualityComparer comparer) 17 | { 18 | return new HashSet(source, comparer); 19 | } 20 | 21 | /// 22 | /// Creates a hashset from given sequence. 23 | /// 24 | [return: NotNull] 25 | public static HashSet ToHashSet([NotNull] this IEnumerable source) => source.ToHashSet(EqualityComparer.Default); 26 | 27 | /// 28 | /// Adds items to the set and returns the number of items that were successfully added. 29 | /// 30 | public static int AddRange([NotNull] this ISet source, [NotNull] IEnumerable items) 31 | { 32 | return items.Count(source.Add); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Tyrrrz.Extensions 8 | { 9 | /// 10 | /// Extensions for . 11 | /// 12 | public static class StringExtensions 13 | { 14 | /// 15 | /// Indicates whether a string is null or empty. 16 | /// 17 | public static bool IsNullOrEmpty([NotNullWhen(false), MaybeNull] this string s) => string.IsNullOrEmpty(s); 18 | 19 | /// 20 | /// Indicates whether a string is either null, empty, or whitespace. 21 | /// 22 | public static bool IsNullOrWhiteSpace([NotNullWhen(false), MaybeNull] this string s) => string.IsNullOrWhiteSpace(s); 23 | 24 | /// 25 | /// Returns an empty string if given a null string, otherwise returns given string. 26 | /// 27 | [return: NotNull] 28 | public static string EmptyIfNull([MaybeNull] this string s) => s ?? string.Empty; 29 | 30 | /// 31 | /// Determines whether the string only consists of digits. 32 | /// 33 | public static bool IsNumeric([NotNull] this string s) 34 | { 35 | return s.ToCharArray().All(char.IsDigit); 36 | } 37 | 38 | /// 39 | /// Determines whether the string only consists of letters. 40 | /// 41 | public static bool IsAlphabetic([NotNull] this string s) 42 | { 43 | return s.ToCharArray().All(char.IsLetter); 44 | } 45 | 46 | /// 47 | /// Determines whether the string only consists of letters and/or digits. 48 | /// 49 | public static bool IsAlphanumeric([NotNull] this string s) 50 | { 51 | return s.ToCharArray().All(char.IsLetterOrDigit); 52 | } 53 | 54 | /// 55 | /// Removes all leading occurrences of a substring in the given string. 56 | /// 57 | [return: NotNull] 58 | public static string TrimStart([NotNull] this string s, [NotNull] string sub, 59 | StringComparison comparison = StringComparison.Ordinal) 60 | { 61 | while (s.StartsWith(sub, comparison)) 62 | s = s.Substring(sub.Length); 63 | 64 | return s; 65 | } 66 | 67 | /// 68 | /// Removes all trailing occurrences of a substring in the given string. 69 | /// 70 | [return: NotNull] 71 | public static string TrimEnd([NotNull] this string s, [NotNull] string sub, 72 | StringComparison comparison = StringComparison.Ordinal) 73 | { 74 | while (s.EndsWith(sub, comparison)) 75 | s = s.Substring(0, s.Length - sub.Length); 76 | 77 | return s; 78 | } 79 | 80 | /// 81 | /// Removes all leading and trailing occurrences of a substring in the given string. 82 | /// 83 | [return: NotNull] 84 | public static string Trim([NotNull] this string s, [NotNull] string sub, 85 | StringComparison comparison = StringComparison.Ordinal) 86 | { 87 | return s.TrimStart(sub, comparison).TrimEnd(sub, comparison); 88 | } 89 | 90 | /// 91 | /// Reverses order of characters in a string. 92 | /// 93 | [return: NotNull] 94 | public static string Reverse([NotNull] this string s) 95 | { 96 | // If length is 1 char or less - return same string 97 | if (s.Length <= 1) 98 | return s; 99 | 100 | // Concat a new string 101 | var sb = new StringBuilder(s.Length); 102 | for (var i = s.Length - 1; i >= 0; i--) 103 | sb.Append(s[i]); 104 | 105 | return sb.ToString(); 106 | } 107 | 108 | /// 109 | /// Returns a string formed by repeating the given string given number of times. 110 | /// 111 | [return: NotNull] 112 | public static string Repeat([NotNull] this string s, int count) 113 | { 114 | // If count is 0 - return empty string 115 | if (count == 0) 116 | return string.Empty; 117 | 118 | // Concat a new string 119 | var sb = new StringBuilder(s.Length * count); 120 | for (var i = 0; i < count; i++) 121 | sb.Append(s); 122 | 123 | return sb.ToString(); 124 | } 125 | 126 | /// 127 | /// Returns a string formed by repeating the given character given number of times. 128 | /// 129 | [return: NotNull] 130 | public static string Repeat(this char c, int count) 131 | { 132 | // If count is 0 - return empty string 133 | if (count == 0) 134 | return string.Empty; 135 | 136 | return new string(c, count); 137 | } 138 | 139 | /// 140 | /// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another specified string. 141 | /// 142 | [return: NotNull] 143 | public static string Replace([NotNull] this string s, [NotNull] string oldValue, [NotNull] string newValue, 144 | StringComparison comparison = StringComparison.Ordinal) 145 | { 146 | var sb = new StringBuilder(); 147 | 148 | var offset = 0; 149 | while (true) 150 | { 151 | // Find the next occurence of old value 152 | var index = s.IndexOf(oldValue, offset, comparison); 153 | 154 | // If not found - append the rest of the string and return 155 | if (index < 0) 156 | { 157 | sb.Append(s, offset, s.Length - offset); 158 | return sb.ToString(); 159 | } 160 | 161 | // Append a portion of the string since last occurence until this one 162 | sb.Append(s, offset, index - offset); 163 | 164 | // Append new value 165 | sb.Append(newValue); 166 | 167 | // Advance offset 168 | offset = index + oldValue.Length; 169 | } 170 | } 171 | 172 | /// 173 | /// Retrieves a substring that ends at the position of first occurrence of the given other string. 174 | /// 175 | [return: NotNull] 176 | public static string SubstringUntil([NotNull] this string s, [NotNull] string sub, 177 | StringComparison comparison = StringComparison.Ordinal) 178 | { 179 | // Find substring 180 | var index = s.IndexOf(sub, comparison); 181 | 182 | // If not found - return whole string 183 | if (index < 0) 184 | return s; 185 | 186 | // Otherwise - return portion of the string until index 187 | return s.Substring(0, index); 188 | } 189 | 190 | /// 191 | /// Retrieves a substring that starts at the position of first occurrence of the given other string. 192 | /// 193 | [return: NotNull] 194 | public static string SubstringAfter([NotNull] this string s, [NotNull] string sub, 195 | StringComparison comparison = StringComparison.Ordinal) 196 | { 197 | // Find substring 198 | var index = s.IndexOf(sub, comparison); 199 | 200 | // If not found - return empty string 201 | if (index < 0) 202 | return string.Empty; 203 | 204 | // Otherwise - return portion of the string after index 205 | return s.Substring(index + sub.Length, s.Length - index - sub.Length); 206 | } 207 | 208 | /// 209 | /// Retrieves a substring that ends at the position of last occurrence of the given other string. 210 | /// 211 | [return: NotNull] 212 | public static string SubstringUntilLast([NotNull] this string s, [NotNull] string sub, 213 | StringComparison comparsion = StringComparison.Ordinal) 214 | { 215 | // Find substring 216 | var index = s.LastIndexOf(sub, comparsion); 217 | 218 | // If not found - return whole string 219 | if (index < 0) 220 | return s; 221 | 222 | // Otherwise - return portion of the string until index 223 | return s.Substring(0, index); 224 | } 225 | 226 | /// 227 | /// Retrieves a substring that starts at the position of last occurrence of the given other string. 228 | /// 229 | [return: NotNull] 230 | public static string SubstringAfterLast([NotNull] this string s, [NotNull] string sub, 231 | StringComparison comparsion = StringComparison.Ordinal) 232 | { 233 | // Find substring 234 | var index = s.LastIndexOf(sub, comparsion); 235 | 236 | // If not found - return empty string 237 | if (index < 0) 238 | return string.Empty; 239 | 240 | // Otherwise - return portion of the string after index 241 | return s.Substring(index + sub.Length, s.Length - index - sub.Length); 242 | } 243 | 244 | /// 245 | /// Discards null, empty and whitespace strings from a sequence. 246 | /// 247 | [return: NotNull] 248 | public static IEnumerable ExceptNullOrWhiteSpace([NotNull] this IEnumerable source) 249 | { 250 | return source.Where(s => !IsNullOrWhiteSpace(s)); 251 | } 252 | 253 | /// 254 | /// Splits string using given separators, discarding empty entries. 255 | /// 256 | [return: NotNull] 257 | public static string[] Split([NotNull] this string s, [NotNull] params string[] separators) 258 | { 259 | return s.Split(separators, StringSplitOptions.RemoveEmptyEntries); 260 | } 261 | 262 | /// 263 | /// Splits string using given separators, discarding empty entries. 264 | /// 265 | [return: NotNull] 266 | public static string[] Split([NotNull] this string s, [NotNull] params char[] separators) 267 | { 268 | return s.Split(separators, StringSplitOptions.RemoveEmptyEntries); 269 | } 270 | 271 | /// 272 | /// Returns a string formed by joining elements of a sequence using the given separator. 273 | /// 274 | [return: NotNull] 275 | public static string JoinToString([NotNull] this IEnumerable source, [NotNull] string separator) 276 | { 277 | return string.Join(separator, source); 278 | } 279 | } 280 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tyrrrz.Extensions 8 | { 9 | /// 10 | /// Extensions for and . 11 | /// 12 | public static class TaskExtensions 13 | { 14 | /// 15 | /// Executes a task asynchronously on all elements of a sequence in parallel and returns results. 16 | /// 17 | [return: NotNull] 18 | public static async Task> ParallelSelectAsync([NotNull] this IEnumerable source, 19 | [NotNull] Func> taskFunc) 20 | { 21 | return await Task.WhenAll(source.Select(taskFunc)).ConfigureAwait(false); 22 | } 23 | 24 | /// 25 | /// Executes a task asynchronously on all elements of a sequence in parallel. 26 | /// 27 | [return: NotNull] 28 | public static async Task ParallelForEachAsync([NotNull] this IEnumerable source, 29 | [NotNull] Func taskFunc) 30 | { 31 | await Task.WhenAll(source.Select(taskFunc)).ConfigureAwait(false); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/Tyrrrz.Extensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net45;netstandard1.0;netstandard2.0;netstandard2.1 5 | 1.6.5 6 | Tyrrrz 7 | $(Company) 8 | Copyright (C) Alexey Golub 9 | Extensions for rapid development 10 | extensions net standard core 11 | https://github.com/Tyrrrz/Extensions 12 | https://github.com/Tyrrrz/Extensions/blob/master/Changelog.md 13 | MIT 14 | https://github.com/Tyrrrz/Extensions 15 | git 16 | False 17 | True 18 | latest 19 | enable 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Tyrrrz.Extensions/UriExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace Tyrrrz.Extensions 8 | { 9 | /// 10 | /// Extensions for . 11 | /// 12 | public static class UriExtensions 13 | { 14 | /// 15 | /// Converts a string to . 16 | /// 17 | [return: NotNull] 18 | public static Uri ToUri([NotNull] this string uri) 19 | { 20 | return new UriBuilder(uri).Uri; 21 | } 22 | 23 | /// 24 | /// Converts a string to relative . 25 | /// 26 | [return: NotNull] 27 | public static Uri ToUri([NotNull] this string uri, [NotNull] Uri baseUri) 28 | { 29 | return new Uri(baseUri, new Uri(uri, UriKind.Relative)); 30 | } 31 | 32 | /// 33 | /// Converts a string to a relative with the other string representing base URI. 34 | /// 35 | [return: NotNull] 36 | public static Uri ToUri([NotNull] this string uri, [NotNull] string baseUri) 37 | { 38 | return uri.ToUri(baseUri.ToUri()); 39 | } 40 | 41 | /// 42 | /// Rewrites URI by setting a query parameter to given value. 43 | /// 44 | [return: NotNull] 45 | public static Uri SetQueryParameter([NotNull] this Uri uri, 46 | [NotNull] string key, [MaybeNull] string value) 47 | { 48 | // Convert URI to string 49 | var uriString = uri.ToString(); 50 | 51 | // Find existing parameter 52 | var existingMatch = Regex.Match(uriString, $@"[?&]({Regex.Escape(key)}=?.*?)(?:&|/|$)"); 53 | 54 | // If parameter is already set - replace with new value 55 | if (existingMatch.Success) 56 | { 57 | // Get the first group 58 | var group = existingMatch.Groups[1]; 59 | 60 | // Remove existing 61 | uriString = uriString.Remove(group.Index, group.Length); 62 | 63 | // Insert new one 64 | uriString = uriString.Insert(group.Index, $"{key}={value}"); 65 | } 66 | // If parameter is not set yet - append it to the end 67 | else 68 | { 69 | // See if there are other query parameters 70 | var hasOtherParams = uriString.IndexOf('?') >= 0; 71 | 72 | // If there are - append '&' 73 | if (hasOtherParams) 74 | uriString += '&'; 75 | // Otherwise - append '?' 76 | else 77 | uriString += '?'; 78 | 79 | // Append parameter 80 | uriString += $"{key}={value}"; 81 | } 82 | 83 | return new Uri(uriString); 84 | } 85 | 86 | /// 87 | /// Rewrites URI by setting a route parameter to given value. 88 | /// 89 | [return: NotNull] 90 | public static Uri SetRouteParameter([NotNull] this Uri uri, 91 | [NotNull] string key, [MaybeNull] string value) 92 | { 93 | // Convert URI to string 94 | var uriString = uri.ToString(); 95 | 96 | // Find existing parameter 97 | var existingMatch = Regex.Match(uriString, $@"/({Regex.Escape(key)}/?.*?)(?:/|$)"); 98 | 99 | // If parameter is already set - replace with new value 100 | if (existingMatch.Success) 101 | { 102 | // Get the first group 103 | var group = existingMatch.Groups[1]; 104 | 105 | // Remove existing 106 | uriString = uriString.Remove(group.Index, group.Length); 107 | 108 | // Insert new one 109 | uriString = uriString.Insert(group.Index, $"{key}/{value}"); 110 | } 111 | // If parameter is not set yet - append it to the end 112 | else 113 | { 114 | // If the URI doesn't end with a slash - append it 115 | if (uriString.ToCharArray().LastOrDefault() != '/') 116 | uriString += '/'; 117 | 118 | // Assemble new query string 119 | uriString += $"{key}/{value}"; 120 | } 121 | 122 | return new Uri(uriString); 123 | } 124 | 125 | #if !NETSTANDARD1_0 126 | /// 127 | /// Opens specified URL using default browser. 128 | /// 129 | public static void OpenInBrowser([NotNull] this Uri uri) 130 | { 131 | var startInfo = new ProcessStartInfo(uri.ToString()) 132 | { 133 | UseShellExecute = true 134 | }; 135 | 136 | using (Process.Start(startInfo)) 137 | { 138 | } 139 | } 140 | #endif 141 | } 142 | } -------------------------------------------------------------------------------- /Tyrrrz.Extensions/XmlExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | 5 | namespace Tyrrrz.Extensions 6 | { 7 | /// 8 | /// Extensions for LINQ to XML. 9 | /// 10 | public static class XmlExtensions 11 | { 12 | /// 13 | /// Returns a new element with namespaces recursively stripped from tags and attributes. 14 | /// 15 | [return: NotNull] 16 | public static XElement StripNamespaces([NotNull] this XElement element) 17 | { 18 | // Based on http://stackoverflow.com/a/1147012 19 | 20 | var result = new XElement(element); 21 | foreach (var e in result.DescendantsAndSelf()) 22 | { 23 | // Strip namespace from name 24 | e.Name = XNamespace.None.GetName(e.Name.LocalName); 25 | 26 | // Strip namespaces from attributes 27 | var attributes = e.Attributes() 28 | .Where(a => !a.IsNamespaceDeclaration) 29 | .Where(a => a.Name.Namespace != XNamespace.Xml && a.Name.Namespace != XNamespace.Xmlns) 30 | .Select(a => new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value)); 31 | e.ReplaceAttributes(attributes); 32 | } 33 | 34 | return result; 35 | } 36 | } 37 | } --------------------------------------------------------------------------------