Skip to content

Instantly share code, notes, and snippets.

@JohanLarsson
Last active October 27, 2016 20:10
Show Gist options
  • Save JohanLarsson/23f129d38a79c7b685ca to your computer and use it in GitHub Desktop.
Save JohanLarsson/23f129d38a79c7b685ca to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
internal static partial class Ensure
{
[JetBrains.Annotations.ContractAnnotation("halt <= value:null")]
internal static void NotNullOrEmpty<T>(IEnumerable<T> value, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
Ensure.NotNull(value, parameterName);
if (!value.Any())
{
throw new ArgumentNullException(parameterName);
}
}
internal static void MinCount(ICollection value, int min, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
Ensure.NotNull(value, parameterName);
if (value.Count < min)
{
var message = $"Expected {nameof(value)}.{nameof(value.Count)} to be at least {min}";
throw new ArgumentException(parameterName, message);
}
}
internal static void MaxCount(ICollection value, int max, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
Ensure.NotNull(value, parameterName);
if (value.Count > max)
{
var message = $"Expected {nameof(value)}.{nameof(value.Count)} to be less than {max}";
throw new ArgumentException(parameterName, message);
}
}
internal static void Distinct<T>(IEnumerable<T> value, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
Ensure.NotNull(value, parameterName);
if (value.Distinct().Count() != value.Count())
{
var message = $"Expected {nameof(value)} to be have only distinct entries";
throw new ArgumentException(parameterName, message);
}
}
internal static void Distinct<T>(IEnumerable<T> value, Func<T, object> selector, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
Ensure.NotNull(value, parameterName);
if (value.Select(selector).Distinct().Count() != value.Count())
{
var message = $"Expected {nameof(value)} to be have only distinct entries";
throw new ArgumentException(parameterName, message);
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
internal static partial class Ensure
{
internal static void LessThan<T>(T value, T max, string parameterName) where T : IComparable<T>
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (Comparer<T>.Default.Compare(value, max) >= 0)
{
string message = $"Expected {parameterName} to be less than {max}, {parameterName} was {value}";
throw new ArgumentException(message, parameterName);
}
}
internal static void LessThanOrEqual<T>(T value, T max, string parameterName) where T : IComparable<T>
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (Comparer<T>.Default.Compare(value, max) > 0)
{
string message = $"Expected {parameterName} to be less than or equal to {max}, {parameterName} was {value}";
throw new ArgumentException(message, parameterName);
}
}
internal static void GreaterThan<T>(T value, T min, string parameterName) where T : IComparable<T>
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (Comparer<T>.Default.Compare(value, min) <= 0)
{
string message = $"Expected {parameterName} to be greater than {min}, {parameterName} was {value}";
throw new ArgumentException(message, parameterName);
}
}
internal static void GreaterThanOrEqual<T>(T value, T min, string parameterName) where T : IComparable<T>
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (Comparer<T>.Default.Compare(value, min) < 0)
{
string message = $"Expected {parameterName} to be greater than or equal to {min}, {parameterName} was {value}";
throw new ArgumentException(message, parameterName);
}
}
}
using System;
using NUnit.Framework;
public partial class EnsureTests
{
[TestCase(0, 1, null)]
[TestCase(0, 0, "Expected x to be less than 0, x was 0\r\nParameter name: x")]
[TestCase(1, 0, "Expected x to be less than 0, x was 1\r\nParameter name: x")]
public void LessThan(int x, int max, string message)
{
if (message == null)
{
Ensure.LessThan(x, max, nameof(x));
}
else
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.LessThan(x, max, nameof(x)));
Assert.AreEqual(message, ex.Message);
}
}
[TestCase(0, 1, null)]
[TestCase(0, 0, null)]
[TestCase(1, 0, "Expected x to be less than or equal to 0, x was 1\r\nParameter name: x")]
public void LessThanOrEqualTo(int x, int max, string message)
{
if (message == null)
{
Ensure.LessThanOrEqual(x, max, nameof(x));
}
else
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.LessThanOrEqual(x, max, nameof(x)));
Assert.AreEqual(message, ex.Message);
}
}
[TestCase(1, 0, null)]
[TestCase(0, 0, "Expected x to be greater than 0, x was 0\r\nParameter name: x")]
[TestCase(0, 1, "Expected x to be greater than 1, x was 0\r\nParameter name: x")]
public void GreaterThan(int x, int min, string message)
{
if (message == null)
{
Ensure.GreaterThan(x, min, nameof(x));
}
else
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.GreaterThan(x, min, nameof(x)));
Assert.AreEqual(message, ex.Message);
}
}
[TestCase(1, 0, null)]
[TestCase(0, 0, null)]
[TestCase(0, 1, "Expected x to be greater than or equal to 1, x was 0\r\nParameter name: x")]
public void GreaterThanOrEqualTo(int x, int min, string message)
{
if (message == null)
{
Ensure.GreaterThanOrEqual(x, min, nameof(x));
}
else
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.GreaterThanOrEqual(x, min, nameof(x)));
Assert.AreEqual(message, ex.Message);
}
}
}
using System;
using System.Diagnostics;
internal static partial class Ensure
{
[JetBrains.Annotations.ContractAnnotation("halt <= value:null")]
public static void NotNull<T>(T value, string parameter, [CallerMemberName] string caller = null)
where T : class
{
Debug.Assert(!string.IsNullOrEmpty(parameter), "parameter cannot be null");
if (value == null)
{
var message = $"Expected parameter {parameter} in member {caller} to not be null";
throw new ArgumentNullException(parameter, message);
}
}
[JetBrains.Annotations.ContractAnnotation("halt <= value:null")]
public static void NotNull<T>(T? value, string parameter, [CallerMemberName] string caller = null)
where T : struct
{
Debug.Assert(!string.IsNullOrEmpty(parameter), "parameter cannot be null");
if (value == null)
{
var message = $"Expected parameter {parameter} in member {caller} to not be null";
throw new ArgumentNullException(parameter, message);
}
}
internal static void IsTrue(bool condition, string parameterName, string message)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (!condition)
{
if (!string.IsNullOrEmpty(message))
{
throw new ArgumentException(message, parameterName);
}
else
{
throw new ArgumentException(parameterName);
}
}
}
internal static void Equal<T>(T value, T expected, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (!Equals(value, expected))
{
var message = $"Expected {parameterName} to be: {expected.ToStringOrNull()}, was: {value.ToStringOrNull()}";
throw new ArgumentException(message, parameterName);
}
}
internal static void NotEqual<T>(T value, T expected, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (Equals(value, expected))
{
var message = $"Expected {parameterName} to not be: {expected.ToStringOrNull()}";
throw new ArgumentException(message, parameterName);
}
}
private static string ToStringOrNull<T>(this T value)
{
if (value == null)
{
return "null";
}
return value.ToString();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
internal static partial class Ensure
{
internal static void Format(string format, object[] args, string formatParameterName, string argsParameterName)
{
Ensure.NotNullOrEmpty(format, "format");
var items = GetFormatItems(format);
if (!AreItemsIntsZeroToN(items))
{
var joined = string.Join(", ", items.Select(x => $"{{{x}}}"));
var message = $"Expected the format items to be [0..n). They were: {joined}";
throw new ArgumentException(message, $"{formatParameterName},{argsParameterName}");
}
if (items.Count == 0)
{
if (args == null || args.Length == 0)
{
return;
}
var message = $"The format string: {format} contains no arguments but: {string.Join(",", args)} was passed as args";
throw new ArgumentException(message, $"{formatParameterName},{argsParameterName}");
}
if (args == null || args.Length == 0)
{
var message = $"The format string: {format} contains {items.Count} arguments but: no arguments were passed.";
throw new ArgumentException(message, $"{formatParameterName},{argsParameterName}");
}
if (args.Length != items.Count)
{
var message = $"The format string: {format} contains {items.Count} arguments but: {args.Length} arguments were provided";
throw new ArgumentException(message, $"{formatParameterName},{argsParameterName}");
}
}
internal static bool FormatMatches(string format, object[] args)
{
var items = GetFormatItems(format);
if (!AreItemsIntsZeroToN(items))
{
return false;
}
return items.Count == (args?.Length ?? 0);
}
internal static bool AreItemsIntsZeroToN(IReadOnlyCollection<string> items)
{
foreach (var item in items)
{
int index;
if (!int.TryParse(item, out index))
{
return false;
}
if (index < 0 || index >= items.Count)
{
return false;
}
}
return true;
}
internal static IReadOnlyCollection<string> GetFormatItems(string format)
{
var matches = Regex.Matches(format, @"{(?<index>\d+)}");
var items = matches.Cast<Match>()
.Select(x => x.Groups["index"].Value)
.Distinct()
.ToList();
return items;
}
}
using System;
using System.Collections.Generic;
using NUnit.Framework;
public partial class EnsureTests
{
[TestCaseSource(typeof(Valids))]
public void FormatHappyPath(FormatData data)
{
Assert.DoesNotThrow(() => Ensure.Format(data.Format, data.Args, "format", "args"));
}
[TestCaseSource(typeof(Valids))]
public void FormatMatchesHappyPath(FormatData data)
{
Assert.True(Ensure.FormatMatches(data.Format, data.Args));
}
[TestCaseSource(typeof(InValids))]
public void FormatThrows(FormatData data)
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.Format(data.Format, data.Args, "format", "args"));
Console.WriteLine(ex.Message);
}
[TestCaseSource(typeof(InValids))]
public void FormatDoesNotMatch(FormatData data)
{
Assert.False(Ensure.FormatMatches(data.Format, data.Args));
}
private class Valids : List<FormatData>
{
public Valids()
{
Add(new FormatData(@"some string", null));
Add(new FormatData(@"some string", new object[0]));
Add(new FormatData(@"string with {0} parameter", new object[] { 1 }));
Add(new FormatData(@"string with {0} parameter {0} in to places", new object[] { 1 }));
Add(new FormatData(@"string with {0} parameter {1} in to places", new object[] { 2, 2 }));
Add(new FormatData(@"string with {0} parameter {1} in to places {0}", new object[] { 2, 2 }));
Add(new FormatData("string with {0} parameter {1} in {2} places", new object[] { 1, 2, 3 }));
}
}
private class InValids : List<FormatData>
{
public InValids()
{
Add(new FormatData("some string", new object[] { 1 }));
Add(new FormatData("string with {0} parameter", null));
Add(new FormatData("string with {1} parameter", new object[] { 1 }));
Add(new FormatData("string with {0} parameter {2}", new object[] { 1, 2 }));
Add(new FormatData("string with {0} parameter", new object[0]));
Add(new FormatData("string with {0} parameter", new object[] { 1, 2 }));
Add(new FormatData("string with {0} parameter {0} in to places", null));
Add(new FormatData("string with {0} parameter {0} in to places", new object[0]));
Add(new FormatData("string with {0} parameter {0} in to places", new object[] { 1, 2 }));
Add(new FormatData("string with {0} parameter {1} in to places", null));
Add(new FormatData("string with {0} parameter {1} in to places", new object[0]));
Add(new FormatData("string with {0} parameter {1} in to places", new object[] { 1 }));
Add(new FormatData("string with {0} parameter {1} in to places", new object[] { 1, 2, 3 }));
}
}
public class FormatData
{
public readonly string Format;
public readonly object[] Args;
public FormatData(string format, object[] args)
{
Format = format;
Args = args;
}
public override string ToString()
{
var args = Args == null
? "null"
: Args.Length == 0
? "object[0]"
: string.Join(",", Args);
return $"Format: {Format}, Args: {args}";
}
}
}
using System;
using System.Collections.Concurrent;
internal static partial class Ensure
{
public static readonly ConcurrentDictionary<Type, int> Singletons = new ConcurrentDictionary<Type, int>();
internal static void Singleton<T>(T @this)
where T : class
{
var type = @this.GetType();
if (!Singletons.TryAdd(type, 0))
{
var message = $"Expected {type.Name} to be singleton";
throw new InvalidOperationException(message);
}
}
}
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
internal static partial class Ensure
{
[JetBrains.Annotations.ContractAnnotation("halt <= value:null")]
internal static void NotNullOrEmpty(string value, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException(parameterName);
}
}
public static void IsMatch(string text, string pattern, string parameterName)
{
Debug.Assert(!string.IsNullOrEmpty(parameterName), $"{nameof(parameterName)} cannot be null");
if (!Regex.IsMatch(text, pattern))
{
throw new ArgumentException(parameterName);
}
}
}
using System;
using System.Collections.Generic;
using NUnit.Framework;
public class EnsureTests
{
[TestCase(null, true)]
[TestCase("", false)]
[TestCase(1, false)]
public void NotNull(object value, bool expectException)
{
if (expectException)
{
var ex = Assert.Throws<ArgumentNullException>(() => Ensure.NotNull(value, nameof(value)));
Assert.AreEqual("Value cannot be null.\r\nParameter name: value", ex.Message);
}
else
{
Ensure.NotNull(value, nameof(value));
}
}
[TestCase(null, true)]
[TestCase("", true)]
[TestCase("Yeah", false)]
public void NotNullOrEmpty(string value, bool expectException)
{
if (expectException)
{
var ex = Assert.Throws<ArgumentNullException>(() => Ensure.NotNullOrEmpty(value, nameof(value)));
Assert.AreEqual("Value cannot be null.\r\nParameter name: value", ex.Message);
}
else
{
Ensure.NotNullOrEmpty(value, nameof(value));
}
}
[TestCase(false, "Message")]
[TestCase(true, null)]
public void IsTrue(bool isTrue, string message)
{
if (isTrue)
{
Ensure.IsTrue(isTrue, nameof(isTrue), message);
}
else
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.IsTrue(isTrue, nameof(isTrue), message));
Assert.AreEqual(message+ "\r\nParameter name: isTrue", ex.Message);
}
}
[TestCase(1, 2, "Expected value to be: 2, was: 1\r\nParameter name: value")]
[TestCase(1, 1, null)]
public void Equal(object value, object expected, string message)
{
if (message == null)
{
Ensure.Equal(value, expected, nameof(value));
}
else
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.Equal(value, expected, nameof(value)));
Assert.AreEqual(message, ex.Message);
}
}
[TestCase(1, 1, "Expected value to not be: 1\r\nParameter name: value")]
[TestCase(1, 2, null)]
public void NotEqual(object value, object expected, string message)
{
if (message == null)
{
Ensure.NotEqual(value, expected, nameof(value));
}
else
{
var ex = Assert.Throws<ArgumentException>(() => Ensure.NotEqual(value, expected, nameof(value)));
Assert.AreEqual(message, ex.Message);
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
internal static class TypeExt
{
private static readonly ConcurrentDictionary<Type, string> Aliases = new ConcurrentDictionary<Type, string>
{
[typeof (bool)] = "bool",
[typeof (bool?)] = "bool?",
[typeof (byte)] = "byte",
[typeof (byte?)] = "byte?",
[typeof(char)] = "char",
[typeof(char?)] = "char?",
[typeof(double)] = "double",
[typeof(double?)] = "double?",
[typeof(float)] = "float",
[typeof(float?)] = "float?",
[typeof(short)] = "short",
[typeof(short?)] = "short?",
[typeof(int)] = "int",
[typeof(int?)] = "int?",
[typeof(long)] = "long",
[typeof(long?)] = "long?",
[typeof(sbyte)] = "sbyte",
[typeof(sbyte?)] = "sbyte?",
[typeof(ushort)] = "ushort",
[typeof(ushort?)] = "ushort?",
[typeof(uint)] = "uint",
[typeof(uint?)] = "uint?",
[typeof(ulong)] = "ulong",
[typeof(ulong?)] = "ulong?",
};
internal static bool IsEnumerableOfT(this Type type)
{
var iEnumerableOfT = type.GetIEnumerableOfT();
return iEnumerableOfT != null;
}
internal static Type GetItemType(this Type type)
{
var enumerable = type.GetIEnumerableOfT();
if (enumerable == null)
{
var message = $"Trying to get typeof(T) when type is not IEnumerable<T>, type is {type.Name}";
throw new ArgumentException(message, nameof(type));
}
return enumerable.GetGenericArguments()
.Single();
}
private static Type GetIEnumerableOfT(this Type type)
{
var enumerable = type.GetInterfaces()
.Where(i => i.IsGenericType)
.SingleOrDefault(i => i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
return enumerable;
}
/// <summary>
/// Returns nicely formatted type names for generic types.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal static string PrettyName(this Type type)
{
string alias;
if (Aliases.TryGetValue(type, out alias))
{
return alias;
}
if (type.IsGenericType)
{
var arguments = string.Join(", ", type.GenericTypeArguments.Select(PrettyName));
return $"{type.Name.Split('`').First()}<{arguments}>";
}
return type.Name;
}
internal static string FullPrettyName(this Type type)
{
string alias;
if (Aliases.TryGetValue(type, out alias))
{
return alias;
}
if (type.IsGenericType)
{
var arguments = string.Join(", ", type.GenericTypeArguments.Select(FullPrettyName));
return $"{type.FullName.Split('`').First()}<{arguments}>";
}
return type.FullName;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment