diff --git a/src/Immediate.Validations.Shared/Validators/LengthAttribute.cs b/src/Immediate.Validations.Shared/Validators/LengthAttribute.cs
new file mode 100644
index 0000000..db099b1
--- /dev/null
+++ b/src/Immediate.Validations.Shared/Validators/LengthAttribute.cs
@@ -0,0 +1,35 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Immediate.Validations.Shared;
+
+///
+/// Applied to a property to indicate that the value should have a length of exactly .
+///
+///
+/// The maximum length of the .
+///
+public sealed class LengthAttribute(
+ [TargetType]
+ object length
+) : ValidatorAttribute
+{
+ ///
+ /// Validates that the has a length of exactly .
+ ///
+ ///
+ /// The value to validate.
+ ///
+ ///
+ /// The valid length for the string .
+ ///
+ ///
+ /// A indicating whether the property is valid or not, along with an error
+ /// message if the property is not valid.
+ ///
+ [SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Will already by validated by IV first.")]
+ public static (bool Invalid, string? Message) ValidateProperty(string target, int length) =>
+ target.Length == length
+ ? default
+ : (true, $"String is of length '{target.Length}', which is {(target.Length < length ? "shorter" : "longer")} than the allowed length of '{length}'.");
+}
diff --git a/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs b/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs
index 8a56e50..1f6c6ab 100644
--- a/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs
+++ b/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs
@@ -20,15 +20,6 @@ public sealed class MatchAttribute(
object? regex = null
) : ValidatorAttribute
{
- ///
- ///
- ///
- public object? Expr { get; } = expr;
- ///
- ///
- ///
- public object? Regex { get; } = regex;
-
///
/// Validates that the matches a particular pattern.
///
diff --git a/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs b/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs
index eaf87bc..fdb07b7 100644
--- a/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs
+++ b/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs
@@ -15,13 +15,13 @@ object length
) : ValidatorAttribute
{
///
- /// Validates that the is less than .
+ /// Validates that the value should have a length at most .
///
///
/// The value to validate.
///
///
- /// The maximum valid length for the string .
+ /// The maximum valid length for the string .
///
///
/// A indicating whether the property is valid or not, along with an error
diff --git a/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs b/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs
index 087798e..13be4e2 100644
--- a/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs
+++ b/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs
@@ -16,13 +16,13 @@ object length
{
///
- /// Validates that the is less than .
+ /// Validates that the value should have a length at least .
///
///
/// The value to validate.
///
///
- /// The minimum valid length for the string .
+ /// The minimum valid length for the string .
///
///
/// A indicating whether the property is valid or not, along with an error
diff --git a/tests/Immediate.Validations.FunctionalTests/Validators/LengthTests.cs b/tests/Immediate.Validations.FunctionalTests/Validators/LengthTests.cs
new file mode 100644
index 0000000..91f25b1
--- /dev/null
+++ b/tests/Immediate.Validations.FunctionalTests/Validators/LengthTests.cs
@@ -0,0 +1,62 @@
+using Immediate.Validations.Shared;
+using Xunit;
+
+namespace Immediate.Validations.FunctionalTests.Validators;
+
+public sealed partial class LengthTests
+{
+ [Validate]
+ public partial record StringRecord : IValidationTarget
+ {
+ [Length(12)]
+ public required string StringValue { get; init; }
+ }
+
+ [Fact]
+ public void LengthWhenShort()
+ {
+ var instance = new StringRecord { StringValue = "Hello" };
+
+ var errors = StringRecord.Validate(instance);
+
+ Assert.Equal(
+ [
+ new()
+ {
+ PropertyName = nameof(StringRecord.StringValue),
+ ErrorMessage = "String is of length '5', which is shorter than the allowed length of '12'.",
+ }
+ ],
+ errors
+ );
+ }
+
+ [Fact]
+ public void LengthWhenEqual()
+ {
+ var instance = new StringRecord { StringValue = "Hello World!" };
+
+ var errors = StringRecord.Validate(instance);
+
+ Assert.Empty(errors);
+ }
+
+ [Fact]
+ public void LengthWhenLong()
+ {
+ var instance = new StringRecord { StringValue = "Hello World! Hello World! Hello World!" };
+
+ var errors = StringRecord.Validate(instance);
+
+ Assert.Equal(
+ [
+ new()
+ {
+ PropertyName = nameof(StringRecord.StringValue),
+ ErrorMessage = "String is of length '38', which is longer than the allowed length of '12'.",
+ }
+ ],
+ errors
+ );
+ }
+}