forked from UbiquityDotNET/Llvm.NET
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFluentValidationExtensions.cs
More file actions
90 lines (82 loc) · 5.08 KB
/
Copy pathFluentValidationExtensions.cs
File metadata and controls
90 lines (82 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Ubiquity.NET.Extensions
{
// This does NOT use the new C# 14 extension syntax due to several reasons
// 1) Code lens does not work https://github.com/dotnet/roslyn/issues/79006 [Sadly, marked as "not planned" - e.g., dead-end]
// 2) MANY analyzers get things wrong and need to be supressed (CA1000, CA1034, and many others [SAxxxx])
// 3) Many tools (like docfx) don't support the new syntax yet and it isn't clear if they will in the future.
// 4) No clear support for Caller* attributes ([CallerArgumentExpression(...)]).
//
// Bottom line it's a good idea with an incomplete implementation lacking support
// in the overall ecosystem. Don't use it unless you absolutely have to until all
// of that is sorted out.
/// <summary>Extension class to provide Fluent validation of arguments</summary>
/// <remarks>
/// These are similar to many of the built-in support checks except that
/// they use a `Fluent' style to allow validation of parameters used as inputs
/// to other functions that ultimately produce parameters for a base constructor.
/// They also serve to provide validation when using body expressions for property
/// method implementations etc... Though the C# 14 <c>field</c> keyword makes that
/// use mostly a moot point.
/// </remarks>
public static class FluentValidationExtensions
{
/// <summary>Throws an exception if <paramref name="obj"/> is <see langword="null"/></summary>
/// <typeparam name="T">Type of reference parameter to test for</typeparam>
/// <param name="obj">Instance to test</param>
/// <param name="exp">Name or expression of the value in <paramref name="obj"/> [Default: provided by compiler]</param>
/// <returns><paramref name="obj"/></returns>
/// <exception cref="ArgumentNullException"><paramref name="obj"/> is <see langword="null"/></exception>
[DebuggerStepThrough]
public static T ThrowIfNull<T>( [NotNull] this T? obj, [CallerArgumentExpression( nameof( obj ) )] string? exp = null )
{
ArgumentNullException.ThrowIfNull( obj, exp );
return obj;
}
/// <summary>Throws an exception if an argument is outside of a given (Inclusive) range</summary>
/// <typeparam name="T">Type of value to test</typeparam>
/// <param name="self">Value to test</param>
/// <param name="min">Minimum value allowed for <paramref name="self"/></param>
/// <param name="max">Maximum value allowed for <paramref name="self"/></param>
/// <param name="exp">Name or expression of the value in <paramref name="self"/> [Default: provided by compiler]</param>
/// <returns><paramref name="self"/></returns>
[DebuggerStepThrough]
public static T ThrowIfOutOfRange<T>( this T self, T min, T max, [CallerArgumentExpression( nameof( self ) )] string? exp = null )
where T : struct, IComparable<T>
{
ArgumentNullException.ThrowIfNull(self, exp);
ArgumentOutOfRangeException.ThrowIfLessThan( self, min, exp );
ArgumentOutOfRangeException.ThrowIfGreaterThan( self, max, exp );
return self;
}
/// <summary>Tests if an enum is defined or not</summary>
/// <typeparam name="T">Type of value to test</typeparam>
/// <param name="self">Value to test</param>
/// <param name="exp">Name or expression of the value in <paramref name="self"/> [Default: provided by compiler]</param>
/// <returns><paramref name="self"/></returns>
/// <exception cref="InvalidEnumArgumentException">The enumerated value is not defined</exception>
/// <remarks>
/// This is useful to prevent callers from playing tricks with casts, etc... to land with a value
/// that is otherwise undefined. Note: This is mostly useless on an enumeration marked with
/// <see cref="FlagsAttribute"/> as a legit value that is a combination of flags does not have
/// a defined value (Only single bit values do)
/// </remarks>
[DebuggerStepThrough]
public static T ThrowIfNotDefined<T>( this T self, [CallerArgumentExpression( nameof( self ) )] string? exp = null )
where T : struct, Enum
{
ArgumentNullException.ThrowIfNull(self, exp);
// TODO: Move the exception message to a resource for globalization
// This matches the overloaded constructor version but allows for reporting enums with non-int underlying type.
return Enum.IsDefined( typeof(T), self )
? self
: throw new InvalidEnumArgumentException($"The value of argument '{exp}' ({self}) is invalid for Enum of type '{typeof(T)}'");
}
}
}