Skip to content

Commit d343f2d

Browse files
committed
Refactor exceptions to use InvalidCharacterException.
Replaced `ArgumentException` with the new `InvalidCharacterException` for better semantic clarity when handling invalid input. Added `InvalidCharacterException` class and updated related tests accordingly to align with these changes. Updated the changelog to document the addition. Resolves: No entry
1 parent bfbb5eb commit d343f2d

File tree

4 files changed

+145
-42
lines changed

4 files changed

+145
-42
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- Add `InvalidCharacterException` to throw an exception if the input string contains invalid characters.
10+
811
### Changed
912
- Renamed `Luhn.ConvertAlphaNumericToNumeric` to `Luhn.AlphaNumericToNumeric`
1013

src/InvalidCharacterException.cs

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// ----------------------------------------------------------------------------
2+
// <copyright file="InvalidCharacterException.cs" company="Private">
3+
// Copyright (c) 2025 All Rights Reserved
4+
// </copyright>
5+
// <author>Sebastian Walther</author>
6+
// <date>01/01/2025 09:00:26 PM</date>
7+
// ----------------------------------------------------------------------------
8+
9+
#region License
10+
11+
// ----------------------------------------------------------------------------
12+
// Copyright 2025 Sebastian Walther
13+
//
14+
// Permission is hereby granted, free of charge, to any person obtaining a copy
15+
// of this software and associated documentation files (the "Software"), to deal
16+
// in the Software without restriction, including without limitation the rights
17+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18+
// copies of the Software, and to permit persons to whom the Software is
19+
// furnished to do so, subject to the following conditions:
20+
//
21+
// The above copyright notice and this permission notice shall be included in
22+
// all copies or substantial portions of the Software.
23+
//
24+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30+
// THE SOFTWARE.
31+
32+
#endregion
33+
34+
using System;
35+
using System.Runtime.Serialization;
36+
37+
namespace LuhnDotNet
38+
{
39+
/// <summary>
40+
/// The exception that is thrown when an invalid character is encountered within an argument.
41+
/// </summary>
42+
[Serializable]
43+
public class InvalidCharacterException : ArgumentException
44+
{
45+
/// <summary>
46+
/// Initializes a new instance of the <see cref="InvalidCharacterException"/> class.
47+
/// </summary>
48+
public InvalidCharacterException() : base("Invalid character encountered in argument.")
49+
{
50+
}
51+
52+
/// <summary>
53+
/// Initializes a new instance of the <see cref="InvalidCharacterException"/> class with a specified error message.
54+
/// </summary>
55+
/// <param name="message">The message that describes the error.</param>
56+
public InvalidCharacterException(string message) : base(message)
57+
{
58+
}
59+
60+
/// <summary>
61+
/// Initializes a new instance of the <see cref="InvalidCharacterException"/> class with a specified error message
62+
/// and the name of the parameter that caused this exception.
63+
/// </summary>
64+
/// <param name="message">The error message that explains the reason for the exception.</param>
65+
/// <param name="paramName">The name of the parameter that caused the current exception.</param>
66+
public InvalidCharacterException(string message, string paramName) : base(message, paramName)
67+
{
68+
}
69+
70+
/// <summary>
71+
/// Initializes a new instance of the <see cref="InvalidCharacterException"/> class with a specified error message,
72+
/// the parameter name, and a reference to the inner exception that is the cause of this exception.
73+
/// </summary>
74+
/// <param name="message">The error message that explains the reason for the exception.</param>
75+
/// <param name="paramName">The name of the parameter that caused the current exception.</param>
76+
/// <param name="innerException">The exception that is the cause of the current exception
77+
/// or a null reference if no inner exception is specified.</param>
78+
public InvalidCharacterException(string message, string paramName, Exception innerException) : base(message, paramName, innerException)
79+
{
80+
}
81+
82+
/// <summary>
83+
/// Initializes a new instance of the <see cref="InvalidCharacterException"/> class with serialized data.
84+
/// </summary>
85+
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
86+
/// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param>
87+
[Obsolete(
88+
"This constructor is obsolete and will be removed in a future version. Use InvalidCharacterException(string message, string paramName, Exception innerException) instead.",
89+
false)]
90+
protected InvalidCharacterException(SerializationInfo info, StreamingContext context) : base(info, context)
91+
{
92+
}
93+
}
94+
}

src/Luhn.cs

+15-15
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static class Luhn
6363
/// </summary>
6464
/// <param name="number">An identification number w/o check digit.</param>
6565
/// <returns>The calculated Luhn check digit.</returns>
66-
/// <exception cref="ArgumentException"><paramref name="number"/> is not valid.
66+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not valid.
6767
/// It contains none-numeric characters.</exception>
6868
[SuppressMessage("ReSharper", "UnusedMember.Global")]
6969
[SuppressMessage("ReSharper", "HeapView.ObjectAllocation")]
@@ -76,7 +76,7 @@ public static byte ComputeLuhnCheckDigit(this ReadOnlySpan<char> number) =>
7676
/// </summary>
7777
/// <param name="number">An identification number w/o check digit.</param>
7878
/// <returns>The calculated Luhn check digit as a byte.</returns>
79-
/// <exception cref="ArgumentException"><paramref name="number"/> is not valid.
79+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not valid.
8080
/// It contains none-numeric characters.</exception>
8181
[SuppressMessage("ReSharper", "UnusedMember.Global")]
8282
[SuppressMessage("ReSharper", "HeapView.ObjectAllocation")]
@@ -93,7 +93,7 @@ public static byte ComputeLuhnCheckDigit(this string number) =>
9393
/// </summary>
9494
/// <param name="number">An identification number w/o check digit.</param>
9595
/// <returns>The calculated Luhn number.</returns>
96-
/// <exception cref="ArgumentException"><paramref name="number"/> is not valid.
96+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not valid.
9797
/// It contains none-numeric characters.</exception>
9898
[SuppressMessage("ReSharper", "UnusedMember.Global")]
9999
[SuppressMessage("ReSharper", "HeapView.ObjectAllocation")]
@@ -109,7 +109,7 @@ public static string ComputeLuhnNumber(this ReadOnlySpan<char> number)
109109
/// </summary>
110110
/// <param name="number">An identification number w/o check digit.</param>
111111
/// <returns>The calculated Luhn number.</returns>
112-
/// <exception cref="ArgumentException"><paramref name="number"/> is not valid.
112+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not valid.
113113
/// It contains none-numeric characters.</exception>
114114
[SuppressMessage("ReSharper", "UnusedMember.Global")]
115115
[SuppressMessage("ReSharper", "HeapView.ObjectAllocation")]
@@ -134,7 +134,7 @@ public static string ComputeLuhnNumber(this string number)
134134
/// <param name="luhnNumber">An identification number w/ check digit (Luhn Number).</param>
135135
/// <returns><see langword="true" /> if the <paramref name="luhnNumber"/> is valid;
136136
/// otherwise <see langword="false" /></returns>
137-
/// <exception cref="ArgumentException"><paramref name="luhnNumber"/> is not valid.
137+
/// <exception cref="InvalidCharacterException"><paramref name="luhnNumber"/> is not valid.
138138
/// It contains none-numeric characters.</exception>
139139
/// <remarks>The check digit must be at the end of the <paramref name="luhnNumber"/>
140140
/// (on the right side).</remarks>
@@ -150,7 +150,7 @@ public static bool IsValidLuhnNumber(this ReadOnlySpan<char> luhnNumber) =>
150150
/// <param name="luhnNumber">An identification number w/ check digit (Luhn Number).</param>
151151
/// <returns><see langword="true" /> if the <paramref name="luhnNumber"/> is valid;
152152
/// otherwise <see langword="false" /></returns>
153-
/// <exception cref="ArgumentException"><paramref name="luhnNumber"/> is not valid.
153+
/// <exception cref="InvalidCharacterException"><paramref name="luhnNumber"/> is not valid.
154154
/// It contains none-numeric characters.</exception>
155155
/// <remarks>The check digit must be at the end of the <paramref name="luhnNumber"/>
156156
/// (on the right side).</remarks>
@@ -171,7 +171,7 @@ public static bool IsValidLuhnNumber(this string luhnNumber) =>
171171
/// <param name="number">Identification number w/o Luhn check digit</param>
172172
/// <returns><see langword="true" /> if the <paramref name="number"/> is valid;
173173
/// otherwise <see langword="false" /></returns>
174-
/// <exception cref="ArgumentException"><paramref name="number"/> is not valid.
174+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not valid.
175175
/// It contains none-numeric characters.</exception>
176176
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="checkDigit"/> value is greater than 9.
177177
/// The <paramref name="checkDigit"/> value must be between 0 and 9.</exception>
@@ -203,7 +203,7 @@ public static bool IsValidLuhnCheckDigit(this byte checkDigit, ReadOnlySpan<char
203203
/// <param name="number">Identification number w/o Luhn check digit</param>
204204
/// <returns><see langword="true" /> if the <paramref name="number"/> is valid;
205205
/// otherwise <see langword="false" /></returns>
206-
/// <exception cref="ArgumentException"><paramref name="number"/> is not valid.
206+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not valid.
207207
/// It contains none-numeric characters.</exception>
208208
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="checkDigit"/> value is greater than 9.
209209
/// The <paramref name="checkDigit"/> value must be between 0 and 9.</exception>
@@ -240,7 +240,7 @@ public static bool IsValidLuhnCheckDigit(this byte checkDigit, string number)
240240
/// <param name="alphaNumeric">The alphanumeric string to convert.</param>
241241
/// <returns>A numeric string where each letter in the input string is replaced by its decimal ASCII value
242242
/// minus 55.</returns>
243-
/// <exception cref="ArgumentException">The <paramref name="alphaNumeric"/> contains a character
243+
/// <exception cref="InvalidCharacterException">The <paramref name="alphaNumeric"/> contains a character
244244
/// that is neither a letter nor a digit.</exception>
245245
/// <remarks>
246246
/// This method iterates over each character in the input string. If the character is a letter, it is replaced
@@ -268,7 +268,7 @@ public static string AlphaNumericToNumeric(this string alphaNumeric)
268268
}
269269
else
270270
{
271-
throw new ArgumentException($"The character '{c}' is not a letter or a digit!", nameof(alphaNumeric));
271+
throw new InvalidCharacterException($"The character '{c}' is not a letter or a digit!", nameof(alphaNumeric));
272272
}
273273
}
274274

@@ -290,7 +290,7 @@ public static string AlphaNumericToNumeric(this string alphaNumeric)
290290
}
291291
else
292292
{
293-
throw new ArgumentException($"The character '{c}' is not a letter or a digit!", nameof(alphaNumeric));
293+
throw new InvalidCharacterException($"The character '{c}' is not a letter or a digit!", nameof(alphaNumeric));
294294
}
295295
}
296296

@@ -340,13 +340,13 @@ private static IEnumerable<uint> DoubleEverySecondDigit(this IEnumerable<uint> d
340340
/// </summary>
341341
/// <param name="number">An identification number</param>
342342
/// <returns>The trimmed identification number if valid</returns>
343-
/// <exception cref="ArgumentException"><paramref name="number"/> is not a valid number</exception>
343+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not a valid number</exception>
344344
private static string ValidateAndTrimNumber(this string number)
345345
{
346346
string trimmedNumber = number?.Trim();
347347
if (string.IsNullOrWhiteSpace(trimmedNumber) || !Regex.IsMatch(trimmedNumber, @"^\d+$"))
348348
{
349-
throw new ArgumentException($"The string '{number}' is not a number!", nameof(number));
349+
throw new InvalidCharacterException($"The string '{number}' is not a number!", nameof(number));
350350
}
351351

352352
return trimmedNumber;
@@ -359,13 +359,13 @@ private static string ValidateAndTrimNumber(this string number)
359359
/// </summary>
360360
/// <param name="number">An identification number</param>
361361
/// <returns>The trimmed identification number if valid</returns>
362-
/// <exception cref="ArgumentException"><paramref name="number"/> is not a valid number</exception>
362+
/// <exception cref="InvalidCharacterException"><paramref name="number"/> is not a valid number</exception>
363363
private static ReadOnlySpan<char> ValidateAndTrimNumber(this ReadOnlySpan<char> number)
364364
{
365365
var trimmedNumber = number.Trim();
366366
if (trimmedNumber.Length == 0 || !trimmedNumber.IsDigits())
367367
{
368-
throw new ArgumentException($"The string '{number}' is not a number!", nameof(number));
368+
throw new InvalidCharacterException($"The string '{number}' is not a number!", nameof(number));
369369
}
370370

371371
return trimmedNumber;

0 commit comments

Comments
 (0)