Skip to content

Instantly share code, notes, and snippets.

@bwatts
Created January 28, 2023 20:35
Show Gist options
  • Save bwatts/8a7a99484a429f4a93c64d93c70c22ef to your computer and use it in GitHub Desktop.
Save bwatts/8a7a99484a429f4a93c64d93c70c22ef to your computer and use it in GitHub Desktop.
I got annoyed checking if strings are null or whitespace in every message.
[JsonConverter(typeof(TextJsonConverter))]
[TypeConverter(typeof(TextTypeConverter))]
public sealed class Text : IEquatable<Text>, IEquatable<string>, IComparable<Text>, IComparable<string>
{
readonly string _value;
Text(string value) =>
_value = value;
public override string ToString() =>
_value.ToString();
public override int GetHashCode() =>
_value.GetHashCode();
public override bool Equals(object? obj) =>
obj switch
{
Text other => Equals(other),
string other => Equals(other),
_ => false
};
public bool Equals(Text? other) =>
_value == other?._value;
public bool Equals(string? other) =>
_value == other;
public int CompareTo(Text? other) =>
other is null ? 1 : _value.CompareTo(other._value);
public int CompareTo(string? other) =>
other is null ? 1 : _value.CompareTo(other);
//
// Static API
//
public static bool TryFrom(string? value, [NotNullWhen(true)] out Text? text) =>
(text = !string.IsNullOrWhiteSpace(value) ? new Text(value) : null) is not null;
public static Text From(string value) =>
TryFrom(value, out var text) ? text : throw new ArgumentOutOfRangeException(nameof(value));
public static bool operator ==(Text? x, Text? y) => EqualityComparer<Text?>.Default.Equals(x, y);
public static bool operator !=(Text? x, Text? y) => !(x == y);
public static bool operator <(Text? x, Text? y) => Comparer<Text?>.Default.Compare(x, y) < 0;
public static bool operator >(Text? x, Text? y) => Comparer<Text?>.Default.Compare(x, y) > 0;
public static bool operator <=(Text? x, Text? y) => Comparer<Text?>.Default.Compare(x, y) <= 0;
public static bool operator >=(Text? x, Text? y) => Comparer<Text?>.Default.Compare(x, y) >= 0;
public static bool operator ==(Text? x, string? y) => EqualityComparer<string?>.Default.Equals(x, y);
public static bool operator !=(Text? x, string? y) => !(x == y);
public static bool operator <(Text? x, string? y) => Comparer<string?>.Default.Compare(x?._value, y) < 0;
public static bool operator >(Text? x, string? y) => Comparer<string?>.Default.Compare(x?._value, y) > 0;
public static bool operator <=(Text? x, string? y) => Comparer<string?>.Default.Compare(x?._value, y) <= 0;
public static bool operator >=(Text? x, string? y) => Comparer<string?>.Default.Compare(x?._value, y) >= 0;
public static explicit operator Text(string value) => From(value);
public static implicit operator string(Text value) => value.ToString();
//
// Converters
//
sealed class TextJsonConverter : JsonConverter<Text>
{
public override Text? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
TryFrom(reader.GetString(), out var text) ? text : null;
public override void Write(Utf8JsonWriter writer, Text value, JsonSerializerOptions options) =>
writer.WriteStringValue(value.ToString());
}
sealed class TextTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) =>
sourceType == typeof(string);
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) =>
destinationType == typeof(string);
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) =>
value is string s ? From(s) : base.ConvertFrom(context, culture, value);
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) =>
destinationType == typeof(string) && value is Text text
? text.ToString()
: base.ConvertTo(context, culture, value, destinationType);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment