Skip to content

String values extensions #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 17 additions & 126 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
: Copied from https://github.com/dotnet/aspnetcore/blob/4bc867dee200dffb10c9e3693b875311407bdfc6/.editorconfig
; https://github.com/dotnet/aspnetcore/blob/main/.editorconfig
;
; EditorConfig to support per-solution formatting.
; Use the EditorConfig VS add-in to make this work.
; http://editorconfig.org/
;
; Here are some resources for what's supported for .NET/C#
; https://kent-boogaart.com/blog/editorconfig-reference-for-c-developers
; https://learn.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference
;
; Be **careful** editing this because some of the rules don't support adding a severity level
; For instance if you change to `dotnet_sort_system_directives_first = true:warning` (adding `:warning`)
; then the rule will be silently ignored.

; ###############################################################################################################
; IDE0005 (Remove unnecessary usings/imports) disabled waiting for https://github.com/dotnet/roslyn/issues/41640
; IDE0005 (Remove unnecessary usings/imports) disabled waiting for https://github.com/dotnet/roslyn/issues/41640
; CA2007 changed to silent
; ###############################################################################################################

; This is the default for the codeline.
root = true

[*]
Expand All @@ -26,18 +14,21 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.{ps1,psm1}]
indent_size = 4

[*.sh]
indent_size = 4
end_of_line = lf

[*.{cmd,bat}]
end_of_line = crlf
[*.{razor,cshtml}]
indent_size = 4
charset = utf-8-bom

[*.cs]
indent_size = 4
dotnet_sort_system_directives_first = true

# Resharper
dotnet_remove_unnecessary_suppression_exclusions = category: ReSharper
dotnet_sort_system_directives_first = true

# Don't use this. qualifier
dotnet_style_qualification_for_field = false:suggestion
Expand Down Expand Up @@ -88,7 +79,6 @@ dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = priva
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case

[*.cs]

# SYSLIB1054: Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
dotnet_diagnostic.SYSLIB1054.severity = warning
Expand Down Expand Up @@ -227,7 +217,7 @@ dotnet_diagnostic.CA1857.severity = warning
dotnet_diagnostic.CA1858.severity = warning

# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = warning
dotnet_diagnostic.CA2007.severity = silent

# CA2008: Do not create tasks without passing a TaskScheduler
dotnet_diagnostic.CA2008.severity = warning
Expand Down Expand Up @@ -322,7 +312,7 @@ dotnet_diagnostic.IDE0060.severity = warning
dotnet_diagnostic.IDE0062.severity = warning

# IDE0073: File header
dotnet_diagnostic.IDE0073.severity = error
dotnet_diagnostic.IDE0073.severity = warning
file_header_template = Copyright (c) Alessandro Ghidini. All rights reserved.\nSPDX-License-Identifier: MIT.

# IDE0161: Convert to file-scoped namespace
Expand All @@ -335,107 +325,8 @@ dotnet_diagnostic.IDE0200.severity = warning
dotnet_style_allow_multiple_blank_lines_experimental = false
dotnet_diagnostic.IDE2000.severity = warning

[test/**/*.cs]
# CA1018: Mark attributes with AttributeUsageAttribute
dotnet_diagnostic.CA1018.severity = suggestion
# CA1507: Use nameof to express symbol names
dotnet_diagnostic.CA1507.severity = suggestion
# CA1510: Use ArgumentNullException throw helper
dotnet_diagnostic.CA1510.severity = suggestion
# CA1511: Use ArgumentException throw helper
dotnet_diagnostic.CA1511.severity = suggestion
# CA1512: Use ArgumentOutOfRangeException throw helper
dotnet_diagnostic.CA1512.severity = suggestion
# CA1513: Use ObjectDisposedException throw helper
dotnet_diagnostic.CA1513.severity = suggestion
# CA1802: Use literals where appropriate
dotnet_diagnostic.CA1802.severity = suggestion
# CA1805: Do not initialize unnecessarily
dotnet_diagnostic.CA1805.severity = suggestion
# CA1810: Do not initialize unnecessarily
dotnet_diagnostic.CA1810.severity = suggestion
# CA1822: Make member static
dotnet_diagnostic.CA1822.severity = suggestion
# CA1823: Avoid zero-length array allocations
dotnet_diagnostic.CA1825.severity = suggestion
# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly
dotnet_diagnostic.CA1826.severity = suggestion
# CA1827: Do not use Count() or LongCount() when Any() can be used
dotnet_diagnostic.CA1827.severity = suggestion
# CA1829: Use Length/Count property instead of Count() when available
dotnet_diagnostic.CA1829.severity = suggestion
# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
dotnet_diagnostic.CA1831.severity = suggestion
# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
dotnet_diagnostic.CA1832.severity = suggestion
# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
dotnet_diagnostic.CA1833.severity = suggestion
# CA1834: Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1834.severity = suggestion
# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'
dotnet_diagnostic.CA1835.severity = suggestion
# CA1837: Use 'Environment.ProcessId'
dotnet_diagnostic.CA1837.severity = suggestion
# CA1838: Avoid 'StringBuilder' parameters for P/Invokes
dotnet_diagnostic.CA1838.severity = suggestion
# CA1841: Prefer Dictionary.Contains methods
dotnet_diagnostic.CA1841.severity = suggestion
# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream'
dotnet_diagnostic.CA1844.severity = suggestion
# CA1845: Use span-based 'string.Concat'
dotnet_diagnostic.CA1845.severity = suggestion
# CA1846: Prefer AsSpan over Substring
dotnet_diagnostic.CA1846.severity = suggestion
# CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
dotnet_diagnostic.CA1847.severity = suggestion
# CA1852: Seal internal types
dotnet_diagnostic.CA1852.severity = suggestion
# CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method
dotnet_diagnostic.CA1854.severity = suggestion
# CA1855: Prefer 'Clear' over 'Fill'
dotnet_diagnostic.CA1855.severity = suggestion
# CA1856: Incorrect usage of ConstantExpected attribute
dotnet_diagnostic.CA1856.severity = suggestion
# CA1857: A constant is expected for the parameter
dotnet_diagnostic.CA1857.severity = suggestion
# CA1858: Use 'StartsWith' instead of 'IndexOf'
dotnet_diagnostic.CA1858.severity = suggestion
# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = suggestion
# CA2008: Do not create tasks without passing a TaskScheduler
dotnet_diagnostic.CA2008.severity = suggestion
# CA2012: Use ValueTask correctly
dotnet_diagnostic.CA2012.severity = suggestion
# CA2022: Avoid inexact read with `Stream.Read`
dotnet_diagnostic.CA2022.severity = suggestion
# CA2201: Do not raise reserved exception types
dotnet_diagnostic.CA2201.severity = suggestion
# CA2249: Use string.Contains instead of string.IndexOf to improve readability.
dotnet_diagnostic.CA2249.severity = suggestion
# IDE0005: Remove unnecessary usings
dotnet_diagnostic.IDE0005.severity = suggestion
# IDE0020: Use pattern matching to avoid is check followed by a cast (with variable)
dotnet_diagnostic.IDE0020.severity = suggestion
# IDE0029: Use coalesce expression (non-nullable types)
dotnet_diagnostic.IDE0029.severity = suggestion
# IDE0030: Use coalesce expression (nullable types)
dotnet_diagnostic.IDE0030.severity = suggestion
# IDE0031: Use null propagation
dotnet_diagnostic.IDE0031.severity = suggestion
# IDE0038: Use pattern matching to avoid is check followed by a cast (without variable)
dotnet_diagnostic.IDE0038.severity = suggestion
# IDE0044: Make field readonly
dotnet_diagnostic.IDE0044.severity = suggestion
# IDE0051: Remove unused private members
dotnet_diagnostic.IDE0051.severity = suggestion
# IDE0059: Unnecessary assignment to a value
dotnet_diagnostic.IDE0059.severity = suggestion
# IDE0060: Remove unused parameters
dotnet_diagnostic.IDE0060.severity = suggestion
# IDE0062: Make local function static
dotnet_diagnostic.IDE0062.severity = suggestion
# IDE0200: Lambda expression can be removed
dotnet_diagnostic.IDE0200.severity = suggestion

# CA2016: Forward the 'CancellationToken' parameter to methods that take one
dotnet_diagnostic.CA2016.severity = suggestion
[{samples/**.cs}]
dotnet_diagnostic.IDE0005.severity = silent
dotnet_diagnostic.IDE0073.severity = silent
dotnet_diagnostic.IDE2000.severity = silent
6 changes: 2 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
<ItemGroup>
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="FluentAssertions" Version="7.0.0-alpha.4" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="9.0.0-rc.1.24431.7" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="Sotsera.Sources.Common" Version="0.0.0-local">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Sotsera.Sources.Common" Version="0.0.0-local" />
<PackageVersion Include="xunit" Version="2.9.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0-pre.30" />
</ItemGroup>
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

A **source only** package for common classes and utilities.

[![GitHub license](https://img.shields.io/github/license/sotsera/sotsera.sources.common)](https://github.com/sotsera/sotsera.sources.common/actions/blob/main/LICENSE)
![example workflow](https://github.com/sotsera/sotsera.sources.common/actions/workflows/publish-nuget-on-tag.yml/badge.svg)
[![Sotsera.Common on NuGet](https://img.shields.io/nuget/v/Sotsera.Sources.Common.svg)](https://www.nuget.org/packages/Sotsera.Sources.Common/)
[![GitHub license](https://img.shields.io/github/license/sotsera/sotsera.sources.common?style=flat-square)](LICENSE)
[![Target](https://img.shields.io/static/v1?label=target&message=net9.0&color=512bd4&logo=.net&style=flat-square)](https://dotnet.microsoft.com/en-us/)
[![GitHub last commit](https://img.shields.io/github/last-commit/sotsera/sotsera.sources.common?display_timestamp=committer&style=flat-square)](https://github.com/sotsera/sotsera.sources.common)
[![NuGet](https://img.shields.io/nuget/v/sotsera.sources.common.svg?style=flat-square)](https://www.nuget.org/packages/sotsera.sources.common/)
[![NuGet Downloads](https://img.shields.io/nuget/dt/sotsera.sources.common?style=flat-square)](https://www.nuget.org/packages/sotsera.sources.common/)
[![GitHub Repo stars](https://img.shields.io/github/stars/sotsera/sotsera.sources.common?style=flat-square)](https://github.com/sotsera/sotsera.sources.common)

### Thanks

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Alessandro Ghidini. All rights reserved.
// SPDX-License-Identifier: MIT.

using System.Runtime.CompilerServices;
using Microsoft.Extensions.Primitives;

#pragma warning disable IDE0130 // Namespace does not match folder structure
namespace Sotsera.Sources.Common.Extensions;
#pragma warning restore IDE0130 // Namespace does not match folder structure

internal static class StringValuesExtensions
{
public static StringValues ThrowIfEmpty(this StringValues argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
{
if (argument.Count == 0 || !argument.ToArray().Any(x => x.IsNonEmpty()))
{
throw new ArgumentException($"The argument '{paramName}' cannot be empty or composed by only empty values", paramName);
}

return argument;
}

public static bool IsEmpty(this StringValues argument)
{
var data = argument.ToArray();

return data.Length == 0 || data.All(x => x.IsEmpty());
}

public static bool IsNonEmpty(this StringValues argument)
{
var data = argument.ToArray();

return data.Length > 0 && data.Any(x => x.IsNonEmpty());
}
}
7 changes: 6 additions & 1 deletion src/Sotsera.Sources.Common/Sotsera.Sources.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<RepositoryType>git</RepositoryType>

<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DevelopmentDependency>true</DevelopmentDependency>
<!--<DevelopmentDependency>true</DevelopmentDependency>--> <!-- Removed because of the dependency on Microsoft.Extensions.Primitives -->
<IncludeBuildOutput>false</IncludeBuildOutput>
<!--<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>-->
<NoWarn>$(NoWarn);NU5128;</NoWarn>
Expand All @@ -33,6 +33,11 @@
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="Sotsera.Sources.Common.Tests.Unit" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Primitives" />
<PackageReference Include="MinVer">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions_005Cmicrosoft_002Eextensions_002Eprimitives/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Sotsera.Sources.Common">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Sotsera.Sources.Common" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void ThrowIfNull_ShouldThrowArgumentNullException_WhenValueIsNull()
[Theory]
[InlineData("ciao")]
[InlineData(true)]
public void ThrowIfNull_ShouldNotThrowArgumentNullException_WhenValueIsNotNull(object? value)
public void ThrowIfNull_ShouldNotThrow_WhenValueIsNotNull(object? value)
{
Action act = () => value.ThrowIfNull();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,47 @@ public void ThrowIfEmpty_ShouldThrowArgumentNullException_WhenValueIsNull()
}

[Theory]
[ClassData(typeof(NullOrWhiteSpaceValueGenerator))]
[ClassData(typeof(WhiteSpaceValueGenerator))]
public void ThrowIfEmpty_ShouldThrowArgumentException_WhenValueIsEmpty(string value)
{
value.IsEmpty().Should().Be(true);
Action act = () => value.ThrowIfEmpty();

act.Should().ThrowExactly<ArgumentException>().WithParameterName(nameof(value));
}

[Theory]
[ClassData(typeof(NonWhitespaceValueGenerator))]
public void ThrowIfEmpty_ShouldNotThrow_WhenValueIsEmpty(string value)
{
Action act = () => value.ThrowIfEmpty();

act.Should().NotThrow();
}

[Theory]
[ClassData(typeof(NullOrWhiteSpaceValueGenerator))]
public void IsEmpty_ShouldReturnTrue_WhenTheValueIsNullOrWhitespace(string? value)
public void IsEmpty_ShouldReturnTrue_WhenValueIsNullOrWhitespace(string? value)
{
value.IsEmpty().Should().BeTrue();
}

[Theory]
[ClassData(typeof(NonWhitespaceValueGenerator))]
public void IsEmpty_ShouldReturnFalse_WhenTheValueIsNonEmpty(string? value)
public void IsEmpty_ShouldReturnFalse_WhenValueIsNonEmpty(string? value)
{
value.IsEmpty().Should().BeFalse();
}

[Theory]
[ClassData(typeof(NullOrWhiteSpaceValueGenerator))]
public void IsNonEmpty_ShouldReturnFalse_WhenTheValueIsNullOrWhitespace(string? value)
public void IsNonEmpty_ShouldReturnFalse_WhenValueIsNullOrWhitespace(string? value)
{
value.IsNonEmpty().Should().BeFalse();
}

[Theory]
[ClassData(typeof(NonWhitespaceValueGenerator))]
public void IsNonEmpty_ShouldReturnTrue_WhenTheValueIsNonEmpty(string? value)
public void IsNonEmpty_ShouldReturnTrue_WhenValueIsNonEmpty(string? value)
{
value.IsNonEmpty().Should().BeTrue();
}
Expand All @@ -60,7 +71,7 @@ public void JoinStrings_ShouldReturnTheExpectedValue(EnumerableStringList list,
list.Values.JoinStrings("; ", includeEmptyValues).Should().Be(expected);
}

private class NullOrWhiteSpaceValueGenerator : TheoryData<string?>
private sealed class NullOrWhiteSpaceValueGenerator : TheoryData<string?>
{
public NullOrWhiteSpaceValueGenerator()
{
Expand All @@ -72,7 +83,18 @@ public NullOrWhiteSpaceValueGenerator()
}
}

private class NonWhitespaceValueGenerator : TheoryData<string?>
private sealed class WhiteSpaceValueGenerator : TheoryData<string?>
{
public WhiteSpaceValueGenerator()
{
Add("");
Add(" ");
Add("\t");
Add(" \t ");
}
}

private sealed class NonWhitespaceValueGenerator : TheoryData<string?>
{
public NonWhitespaceValueGenerator()
{
Expand All @@ -82,7 +104,7 @@ public NonWhitespaceValueGenerator()
}
}

private class StringEnumerableToBeJoinedValueGenerator : TheoryData<EnumerableStringList, bool, string>
private sealed class StringEnumerableToBeJoinedValueGenerator : TheoryData<EnumerableStringList, bool, string>
{
public StringEnumerableToBeJoinedValueGenerator()
{
Expand Down
Loading