Skip to content
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
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Nothing yet.

## [1.20.4] - 2025-04-16

### Added

- Method `StringExtensions.Humanize`.

## [1.20.3] - 2025-04-16

### Fixed
Expand Down Expand Up @@ -281,7 +287,8 @@ Nothing yet.

- Implemented StringExtensions.

[unreleased]: https://github.com/Logitar/Logitar.NET/compare/v1.20.3...HEAD
[unreleased]: https://github.com/Logitar/Logitar.NET/compare/v1.20.4...HEAD
[1.20.4]: https://github.com/Logitar/Logitar.NET/compare/v1.20.3...v1.20.4
[1.20.3]: https://github.com/Logitar/Logitar.NET/compare/v1.20.2...v1.20.3
[1.20.2]: https://github.com/Logitar/Logitar.NET/compare/v1.20.1...v1.20.2
[1.20.1]: https://github.com/Logitar/Logitar.NET/compare/v1.20.0...v1.20.1
Expand Down
7 changes: 4 additions & 3 deletions src/Logitar/Logitar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/Logitar/Logitar.NET</RepositoryUrl>
<AssemblyVersion>7.0.0.0</AssemblyVersion>
<AssemblyVersion>7.1.0.0</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<Version>7.0.0</Version>
<Version>7.1.0</Version>
<NeutralLanguage>en-CA</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageReleaseNotes>Removed `ExceptionDetail` and dependency from `System.Text.Json`.</PackageReleaseNotes>
<PackageReleaseNotes>Added `StringExtensions.Humanize` method.</PackageReleaseNotes>
<PackageTags>logitar net framework</PackageTags>
<PackageProjectUrl>https://github.com/Logitar/Logitar.NET/tree/dev/src/Logitar</PackageProjectUrl>
</PropertyGroup>
Expand Down Expand Up @@ -53,6 +53,7 @@
<ItemGroup>
<Using Include="System.Collections" />
<Using Include="System.Globalization" />
<Using Include="System.Text" />
</ItemGroup>

</Project>
35 changes: 35 additions & 0 deletions src/Logitar/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,41 @@ public static string FromUriSafeBase64(this string s)
/// <returns>The Uri-safe base6 version of the string.</returns>
public static string ToUriSafeBase64(this string s) => s.Replace('+', '-').Replace('/', '_').TrimEnd('=');

/// <summary>
/// Humanizes the specified string by inserting spaces between words. Words are separated by case-change, such as in camelCase and PascalCase.
/// </summary>
/// <param name="s">The camelCase or PascalCase string.</param>
/// <returns>The humanized string.</returns>
public static string Humanize(this string s)
{
List<string> words = new(capacity: s.Length);

StringBuilder word = new();
for (int i = 0; i < s.Length; i++)
{
char? previous = (i > 0) ? s[i - 1] : null;
char current = s[i];
char? next = (i < s.Length - 1) ? s[i + 1] : null;

if (char.IsUpper(current) && ((previous.HasValue && char.IsLower(previous.Value)) || (next.HasValue && char.IsLower(next.Value))))
{
if (word.Length > 0)
{
words.Add(word.ToString());
word.Clear();
}
}

word.Append(current);
}
if (word.Length > 0)
{
words.Add(word.ToString());
}

return string.Join(' ', words);
}

/// <summary>
/// Returns a new string where every character of the specified string has been replaced by the specified mask character (defaults to '*').
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions tests/Logitar.UnitTests/StringExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ public void FromUriSafeBase64_uri_safe_base64_is_correctly_parsed(string base64)
Assert.Equal(bytes, other);
}

[Theory(DisplayName = "Humanize: it should return the correct humanized string.")]
[InlineData("", "")]
[InlineData(" ", " ")]
[InlineData("helloWorld", "hello World")]
[InlineData("HelloWorld1!", "Hello World1!")]
[InlineData("UserID", "User ID")]
public void Humanize_it_returns_the_correct_humanized_string(string input, string expected)
{
Assert.Equal(expected, input.Humanize());
}

[Theory(DisplayName = "Mask: it returns the correct masked string.")]
[InlineData(" ")]
[InlineData("P@s$W0rD", '-')]
Expand Down