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
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
<Import Project="dependencies.props" />
<PropertyGroup>
<VersionPrefix>1</VersionPrefix>
<Version>$(VersionPrefix).4.0</Version>
<Version>$(VersionPrefix).5.0</Version>
<FileVersion>$(Version)</FileVersion>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Company>René Pacios</Company>
<Copyright>Copyright © $(Company) - $([System.DateTime]::Now.Year)</Copyright>
</PropertyGroup>
Expand Down
19 changes: 19 additions & 0 deletions src/Rene.Utils.Core/Extensions/Collections/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace System.Collections.Generic
using Rene.Utils.Core.Resources;
using ComponentModel;
using Data;
using Threading.Tasks;

public static class EnumerableExtensions
{
Expand All @@ -65,6 +66,24 @@ public static void ForEach<T>(this IEnumerable<T> source, Action<T, int> action)
}
}

/// <summary>
/// Extend internal ForEach Method add item index to where callback
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="action"></param>
/// <exception cref="NullReferenceException"></exception>
public static async Task ForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> action)
{
if (source == null) throw new NullReferenceException(string.Format(CultureInfo.CurrentCulture, ExceptionMessages.NulleReferenceExceptioX0, nameof(source)));
if (action == null) throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, ExceptionMessages.NulleReferenceExceptioX0, nameof(action)));

foreach (var item in source)
{
await action(item);

}
}

/// <summary>
/// Get ListItems after apply where to each item
Expand Down
50 changes: 50 additions & 0 deletions src/Rene.Utils.Core/Extensions/Primitives/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ included in all copies or substantial portions of the Software.
*/
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -216,5 +217,54 @@ public static string ToRandomString(this string chars, int length)
}

#endregion

#region Comprobaciones


/// <summary>
/// Checks whether the string contains any of the specified substrings.
/// </summary>
/// <param name="element">The string to evaluate.</param>
/// <param name="substringsToCheck">The substrings to search for.</param>
/// <returns><c>true</c> if at least one substring is found; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentException">Thrown if <paramref name="substringsToCheck"/> is null or empty.</exception>
public static bool ContainsAny(this string element, string[] substringsToCheck)
{
if (string.IsNullOrEmpty(element))
return false;

if(substringsToCheck==null || substringsToCheck.Length==0)
throw new ArgumentException(ExceptionMessages.ArrayNullOrEmptyArgumentFormat, nameof(substringsToCheck));

return substringsToCheck.Any(element.Contains);
}


/// <summary>
/// Inserts spaces into a CamelCase or PascalCase string.
/// Example: "MyTestVariable" → "My Test Variable".
/// </summary>
/// <param name="text">The input string.</param>
/// <returns>A new string with spaces inserted before capital letters.</returns>
public static string AddSpacesToSentence(this string text)
{
if (string.IsNullOrWhiteSpace(text))
return string.Empty;

var sb = new StringBuilder(text.Length * 2);
sb.Append(text[0]);

for (int i = 1; i < text.Length; i++)
{
if (char.IsUpper(text[i]) && text[i - 1] != ' ')
sb.Append(' ');

sb.Append(text[i]);
}

return sb.ToString();
}
#endregion

}
}
9 changes: 9 additions & 0 deletions src/Rene.Utils.Core/Resources/ExceptionMessages.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Rene.Utils.Core/Resources/ExceptionMessages.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ArrayNullOrEmptyArgumentFormat" xml:space="preserve">
<value>Array is Null or Empty</value>
</data>
<data name="KeySizeOutMessage" xml:space="preserve">
<value>The specified key does not have a valid size</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Rene.Utils.Core.UnitTest.Extensions.Collections
{
using FluentAssertions;
using System;
using System.Threading.Tasks;

public class EnumerableExtensionsTest
{
Expand Down Expand Up @@ -92,7 +94,7 @@ public void AnyNotNull_With_Null_Instances()

source.AnyNotNull().Should().BeFalse();
source.Should().BeNull();

}


Expand All @@ -110,17 +112,46 @@ public void AsEnumerableWithIndex()
{
tuple.item.Should().Be(source[i]);
tuple.index.Should().Be(i);

i++;
}

source.Should().HaveCount(i);



}


[Fact]
public void ForEachAsync_With_Null_Instances()
{
List<int> source = null;

source
.Invoking(s => s.ForEachAsync(async w => await Task.CompletedTask))
.Should()
.Throw<NullReferenceException>();
}

[Fact]
public void ForEachAsync_With_Null_Action()
{
var source = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7 };

source
.Invoking(s => s.ForEachAsync(null))
.Should()
.Throw<ArgumentNullException>();
}

[Fact]
public void ForEachAsync_With_Action()
{
var source = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7 };

source.ForEachAsync(async w => await Task.CompletedTask);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -469,5 +469,167 @@ public void ToRandomString_RequireChars_WhiteSpace_Should_Be_As_Spectated()


#endregion ToRandomString

#region ContainsAnyArray

[Fact]
public void ContainsAnyArray_Should_Be_As_Spectated()
{
var input = "Hello World";
var result = input.ContainsAny(new[] { "Hello", "Universe" });
result.Should().BeTrue();
}

[Fact]
public void ContainsAnyArray_Should_Be_False()
{
var input = "Hello World";
var result = input.ContainsAny(new[] { "Universe", "Galaxy" });
result.Should().BeFalse();
}

[Fact]
public void ContainsAnyArray_NullOrEmptyString_Should_Be_False()
{
string input = null;
var result = input.ContainsAny(new[] { "Universe", "Galaxy" });
result.Should().BeFalse();

input = string.Empty;
var result_1 = input.ContainsAny(new[] { "Universe", "Galaxy" });
result_1.Should().BeFalse();
}

[Fact]
public void ContainsAnyArray_NullOrEmptyArray_Should_Be_False()
{
var input = "Hello World";

Action act1 = () => input.ContainsAny(null);
act1.Should()
.Throw<ArgumentException>();
//.WithMessage(ExceptionMessages.ArrayNullOrEmptyArgumentFormat) // opcional: verificar el mensaje

Action act2 = () => input.ContainsAny(Array.Empty<string>());
act2.Should()
.Throw<ArgumentException>();
//.WithMessage(ExceptionMessages.ArrayNullOrEmptyArgumentFormat);
}

[Fact]
public void ContainsAnyArray_Both_NullOrEmpty_Should_Be_False()
{
string input = null;
var result = input.ContainsAny(null);
result.Should().BeFalse();
input = string.Empty;
var result_1 = input.ContainsAny(Array.Empty<string>());
result_1.Should().BeFalse();
}



#endregion

#region AddSpacesToSentence

public class StringExtensions_AddSpacesToSentence_Tests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Returns_Empty_For_Null_Or_Whitespace(string input)
{
// Act
var result = input.AddSpacesToSentence();

// Assert
result.Should().BeEmpty();
}

[Theory]
[InlineData("A", "A")]
[InlineData("a", "a")]
[InlineData("NoUpper", "No Upper")]
[InlineData("myTest", "my Test")]
[InlineData("MyTestVariable", "My Test Variable")]
[InlineData("HelloWorld!", "Hello World!")]
public void Inserts_Space_Before_Uppercase_When_Preceding_Is_Not_Space(string input, string expected)
{
// Act
var result = input.AddSpacesToSentence();

// Assert
result.Should().Be(expected);
}

[Theory]
[InlineData("Already Spaced", "Already Spaced")]
[InlineData("This Is", "This Is")] // conserva espacios dobles ya existentes
[InlineData(" LeadingSpace", " Leading Space")] // agrega espacio antes de 'S' si procede
public void Preserves_Existing_Spaces(string input, string expected)
{
// Act
var result = input.AddSpacesToSentence();

// Assert
result.Should().Be(expected);
}

[Theory]
[InlineData("HTMLParser", "H T M L Parser")]
[InlineData("JSON", "J S O N")]
[InlineData("XMLErrorCode", "X M L Error Code")]
public void Splits_Acronyms_Into_Individual_Letters_With_Spaces(string input, string expected)
{
// Act
var result = input.AddSpacesToSentence();

// Assert
result.Should().Be(expected);
}

[Theory]
[InlineData("number9Test", "number9 Test")]
[InlineData("End2EndTest", "End2 End Test")]
public void Leaves_Digits_And_NonLetters_Intact_Only_Adds_Before_Uppercase(string input, string expected)
{
// Act
var result = input.AddSpacesToSentence();

// Assert
result.Should().Be(expected);
}

[Fact]
public void Does_Not_Remove_Or_Change_Characters_Other_Than_Adding_Spaces_Before_Uppers()
{
// Arrange
var inputs = new[]
{
"MyTestVariable",
"Already Spaced",
"HelloWorld!",
"A",
"myTest",
"HTMLParser"
};

foreach (var input in inputs)
{
// Act
var result = input.AddSpacesToSentence();

// Assert: si quitamos espacios, debe quedar igual que el original sin espacios
result.Replace(" ", string.Empty)
.Should().Be(input.Replace(" ", string.Empty),
$"the function should not alter non-space characters for '{input}'");
}
}
}

#endregion

}
}