Skip to content

Commit 08adfbc

Browse files
committed
feat: add IsEmailAddress overloads for Span<T> and Memory<T>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
1 parent 9e618de commit 08adfbc

File tree

2 files changed

+202
-39
lines changed

2 files changed

+202
-39
lines changed
Lines changed: 129 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,150 @@
1-
using FluentAssertions;
1+
using System;
2+
using FluentAssertions;
23
using Xunit;
34

45
namespace Light.GuardClauses.Tests.StringAssertions;
56

67
public sealed class IsEmailAddressTests
78
{
9+
public static readonly TheoryData<string> InvalidEmailAddresses =
10+
[
11+
null,
12+
"plainaddress",
13+
"#@%^%#$@#$@#.com",
14+
"@domain.com",
15+
"Joe Smith <email@domain.com>",
16+
"email.domain.com",
17+
"email@domain@domain.com",
18+
".email@domain.com",
19+
"email.@domain.com",
20+
"email..email@domain.com",
21+
"email@domain.com (Joe Smith)",
22+
"email@domain",
23+
"email@-domain.com",
24+
"email@111.222.333.44444",
25+
"email@domain..com",
26+
"email@256.256.256.256",
27+
];
28+
829
[Theory]
9-
[InlineData(null)]
10-
[InlineData("plainaddress")]
11-
[InlineData("#@%^%#$@#$@#.com")]
12-
[InlineData("@domain.com")]
13-
[InlineData("Joe Smith <email@domain.com>")]
14-
[InlineData("email.domain.com")]
15-
[InlineData("email@domain@domain.com")]
16-
[InlineData(".email@domain.com")]
17-
[InlineData("email.@domain.com")]
18-
[InlineData("email..email@domain.com")]
19-
[InlineData("email@domain.com (Joe Smith)")]
20-
[InlineData("email@domain")]
21-
[InlineData("email@-domain.com")]
22-
[InlineData("email@111.222.333.44444")]
23-
[InlineData("email@domain..com")]
24-
[InlineData("email@256.256.256.256")] // Invalid IP (values > 255)
30+
[MemberData(nameof(InvalidEmailAddresses))]
2531
public void IsNotValidEmailAddress(string email)
2632
{
2733
var isValid = email.IsEmailAddress();
2834

2935
isValid.Should().BeFalse();
3036
}
3137

38+
public static readonly TheoryData<string> ValidEmailAddresses =
39+
[
40+
"email@domain.com",
41+
"firstname.lastname@domain.com",
42+
"email@subdomain.domain.com",
43+
"firstname+lastname@domain.com",
44+
"email@123.123.123.123",
45+
"1234567890@domain.com",
46+
"email@domain-one.com",
47+
"_______@domain.com",
48+
"email@domain.name",
49+
"email@domain.co.jp",
50+
"firstname-lastname@domain.com",
51+
"email@domain.museum", // Long TLD (>4 chars)
52+
"email@domain.travel", // Another long TLD
53+
"email@domain.photography", // Even longer TLD
54+
"email@[IPv6:2001:db8::1]", // IPv6 format
55+
"\"quoted\"@domain.com", // Quoted local part
56+
"user.name+tag+sorting@example.com", // Gmail-style + addressing
57+
"あいうえお@domain.com", // Unicode character test
58+
];
59+
3260
[Theory]
33-
[InlineData("email@domain.com")]
34-
[InlineData("firstname.lastname@domain.com")]
35-
[InlineData("email@subdomain.domain.com")]
36-
[InlineData("firstname+lastname@domain.com")]
37-
[InlineData("email@123.123.123.123")]
38-
[InlineData("1234567890@domain.com")]
39-
[InlineData("email@domain-one.com")]
40-
[InlineData("_______@domain.com")]
41-
[InlineData("email@domain.name")]
42-
[InlineData("email@domain.co.jp")]
43-
[InlineData("firstname-lastname@domain.com")]
44-
[InlineData("email@domain.museum")] // Long TLD (>4 chars)
45-
[InlineData("email@domain.travel")] // Another long TLD
46-
[InlineData("email@domain.photography")] // Even longer TLD
47-
[InlineData("email@[IPv6:2001:db8::1]")] // IPv6 format
48-
[InlineData("\"quoted\"@domain.com")] // Quoted local part
49-
[InlineData("user.name+tag+sorting@example.com")] // Gmail-style + addressing
50-
[InlineData("あいうえお@domain.com")] // Unicode character test
61+
[MemberData(nameof(ValidEmailAddresses))]
5162
public void IsValidEmailAddress(string email)
5263
{
5364
var isValid = email.IsEmailAddress();
5465

5566
isValid.Should().BeTrue();
5667
}
57-
}
68+
69+
#if NET8_0
70+
[Theory]
71+
[MemberData(nameof(InvalidEmailAddresses))]
72+
public void IsNotValidEmailAddress_ReadOnlySpan(string email)
73+
{
74+
var span = new ReadOnlySpan<char>(email?.ToCharArray() ?? []);
75+
var isValid = span.IsEmailAddress();
76+
77+
isValid.Should().BeFalse();
78+
}
79+
80+
[Theory]
81+
[MemberData(nameof(ValidEmailAddresses))]
82+
public void IsValidEmailAddress_ReadOnlySpan(string email)
83+
{
84+
var span = email.AsSpan();
85+
var isValid = span.IsEmailAddress();
86+
87+
isValid.Should().BeTrue();
88+
}
89+
90+
[Theory]
91+
[MemberData(nameof(InvalidEmailAddresses))]
92+
public void IsNotValidEmailAddress_Span(string email)
93+
{
94+
var span = new Span<char>(email?.ToCharArray() ?? []);
95+
var isValid = span.IsEmailAddress();
96+
97+
isValid.Should().BeFalse();
98+
}
99+
100+
[Theory]
101+
[MemberData(nameof(ValidEmailAddresses))]
102+
public void IsValidEmailAddress_Span(string email)
103+
{
104+
var span = new Span<char>(email.ToCharArray());
105+
var isValid = span.IsEmailAddress();
106+
107+
isValid.Should().BeTrue();
108+
}
109+
110+
[Theory]
111+
[MemberData(nameof(InvalidEmailAddresses))]
112+
public void IsNotValidEmailAddress_Memory(string email)
113+
{
114+
var memory = email?.ToCharArray().AsMemory() ?? Memory<char>.Empty;
115+
var isValid = memory.IsEmailAddress();
116+
117+
isValid.Should().BeFalse();
118+
}
119+
120+
[Theory]
121+
[MemberData(nameof(ValidEmailAddresses))]
122+
public void IsValidEmailAddress_Memory(string email)
123+
{
124+
var memory = email.ToCharArray().AsMemory();
125+
var isValid = memory.IsEmailAddress();
126+
127+
isValid.Should().BeTrue();
128+
}
129+
130+
[Theory]
131+
[MemberData(nameof(InvalidEmailAddresses))]
132+
public void IsNotValidEmailAddress_ReadOnlyMemory(string email)
133+
{
134+
var memory = new ReadOnlyMemory<char>(email?.ToCharArray() ?? []);
135+
var isValid = memory.IsEmailAddress();
136+
137+
isValid.Should().BeFalse();
138+
}
139+
140+
[Theory]
141+
[MemberData(nameof(ValidEmailAddresses))]
142+
public void IsValidEmailAddress_ReadOnlyMemory(string email)
143+
{
144+
var memory = new ReadOnlyMemory<char>(email.ToCharArray());
145+
var isValid = memory.IsEmailAddress();
146+
147+
isValid.Should().BeTrue();
148+
}
149+
#endif
150+
}

Code/Light.GuardClauses/Check.IsEmailAddress.cs

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
using System;
22
using System.Diagnostics.CodeAnalysis;
3+
using System.Runtime.CompilerServices;
34
using System.Text.RegularExpressions;
45
using JetBrains.Annotations;
5-
using System.Runtime.CompilerServices;
66

77
namespace Light.GuardClauses;
88

99
public static partial class Check
1010
{
1111
/// <summary>
1212
/// Checks if the specified string is an email address using the default email regular expression
13-
/// defined in <see cref="RegularExpressions.EmailRegex"/>.
13+
/// defined in <see cref="RegularExpressions.EmailRegex" />.
1414
/// </summary>
1515
/// <param name="emailAddress">The string to be checked if it is an email address.</param>
1616
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -23,9 +23,79 @@ public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress)
2323
/// </summary>
2424
/// <param name="emailAddress">The string to be checked.</param>
2525
/// <param name="emailAddressPattern">The regular expression that determines whether the input string is an email address.</param>
26-
/// <exception cref="ArgumentNullException">Thrown when <paramref name="emailAddressPattern"/> is null.</exception>
26+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="emailAddressPattern" /> is null.</exception>
2727
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2828
[ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")]
2929
public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) =>
3030
emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress);
31+
32+
#if NET8_0
33+
/// <summary>
34+
/// Checks if the specified span is an email address using the default email regular expression
35+
/// defined in <see cref="RegularExpressions.EmailRegex" />.
36+
/// </summary>
37+
/// <param name="emailAddress">The span to be checked.</param>
38+
public static bool IsEmailAddress(this Span<char> emailAddress) =>
39+
RegularExpressions.EmailRegex.IsMatch(emailAddress);
40+
41+
/// <summary>
42+
/// Checks if the specified span is an email address using the provided regular expression for validation.
43+
/// </summary>
44+
/// <param name="emailAddress">The span to be checked.</param>
45+
/// <param name="emailAddressPattern">The regular expression that determines whether the input string is an email address.</param>
46+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="emailAddressPattern" /> is null.</exception>
47+
public static bool IsEmailAddress(this Span<char> emailAddress, Regex emailAddressPattern) =>
48+
emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress);
49+
50+
/// <summary>
51+
/// Checks if the specified span is an email address using the default email regular expression
52+
/// defined in <see cref="RegularExpressions.EmailRegex" />.
53+
/// </summary>
54+
/// <param name="emailAddress">The span to be checked.</param>
55+
public static bool IsEmailAddress(this ReadOnlySpan<char> emailAddress) =>
56+
RegularExpressions.EmailRegex.IsMatch(emailAddress);
57+
58+
/// <summary>
59+
/// Checks if the specified span is an email address using the provided regular expression for validation.
60+
/// </summary>
61+
/// <param name="emailAddress">The span to be checked.</param>
62+
/// <param name="emailAddressPattern">The regular expression that determines whether the input string is an email address.</param>
63+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="emailAddressPattern" /> is null.</exception>
64+
public static bool IsEmailAddress(this ReadOnlySpan<char> emailAddress, Regex emailAddressPattern) =>
65+
emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress);
66+
67+
/// <summary>
68+
/// Checks if the specified character memory is an email address using the default email regular expression
69+
/// defined in <see cref="RegularExpressions.EmailRegex" />.
70+
/// </summary>
71+
/// <param name="emailAddress">The character memory to be checked.</param>
72+
public static bool IsEmailAddress(this Memory<char> emailAddress) =>
73+
RegularExpressions.EmailRegex.IsMatch(emailAddress.Span);
74+
75+
/// <summary>
76+
/// Checks if the specified character memory is an email address using the provided regular expression for validation.
77+
/// </summary>
78+
/// <param name="emailAddress">The character memory to be checked.</param>
79+
/// <param name="emailAddressPattern">The regular expression that determines whether the input string is an email address.</param>
80+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="emailAddressPattern" /> is null.</exception>
81+
public static bool IsEmailAddress(this Memory<char> emailAddress, Regex emailAddressPattern) =>
82+
emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress.Span);
83+
84+
/// <summary>
85+
/// Checks if the specified character memory is an email address using the default email regular expression
86+
/// defined in <see cref="RegularExpressions.EmailRegex" />.
87+
/// </summary>
88+
/// <param name="emailAddress">The character memory to be checked.</param>
89+
public static bool IsEmailAddress(this ReadOnlyMemory<char> emailAddress) =>
90+
RegularExpressions.EmailRegex.IsMatch(emailAddress.Span);
91+
92+
/// <summary>
93+
/// Checks if the specified character memory is an email address using the provided regular expression for validation.
94+
/// </summary>
95+
/// <param name="emailAddress">The character memory to be checked.</param>
96+
/// <param name="emailAddressPattern">The regular expression that determines whether the input string is an email address.</param>
97+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="emailAddressPattern" /> is null.</exception>
98+
public static bool IsEmailAddress(this ReadOnlyMemory<char> emailAddress, Regex emailAddressPattern) =>
99+
emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress.Span);
100+
#endif
31101
}

0 commit comments

Comments
 (0)