Skip to content

Commit

Permalink
Implement ISpanFormatable.
Browse files Browse the repository at this point in the history
  • Loading branch information
Corniel committed Aug 8, 2023
1 parent d8d34b8 commit 50b3569
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 16 deletions.
35 changes: 33 additions & 2 deletions specs/Qowaiv.Specs/Sustainability/EnergyLabel_specs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,39 @@ public void custom_format_provider_is_applied()
[TestCase("U", "B", "B")]
[TestCase("l", "G", "g")]
[TestCase("l", "A++", "a++")]
[TestCase("L", "A++", "a++")]
public void fomrat_dependent(string format, EnergyLabel svo, string formatted)
public void format_dependent(string format, EnergyLabel svo, string formatted)
=> svo.ToString(format).Should().Be(formatted);

#if NET6_0_OR_GREATER

public class Span_formatable
{
[Test]
public void Skips_custom_formatters()
{
Span<char> span = stackalloc char[128];
Svo.EnergyLabel.TryFormat(span, out int charsWritten, default, FormatProvider.CustomFormatter).Should().BeFalse();
charsWritten.Should().Be(0);
}

[Test]
public void formats_empty() => $"{EnergyLabel.Empty}".Should().BeEmpty();

[Test]
public void formats_unknown() => $"{EnergyLabel.Unknown}".Should().Be("?");

[Test]
public void formats_known() => $"{Svo.EnergyLabel:l}".Should().Be("a++");

[Test]
public void Skips_unsuficient_span_sizes()
{
Span<char> span = stackalloc char[2];
Svo.EnergyLabel.TryFormat(span, out int charsWritten, default, TestCultures.Nl_NL).Should().BeFalse();
charsWritten.Should().Be(0);
}
}
#endif
}

public class Is_comparable
Expand Down Expand Up @@ -338,6 +368,7 @@ public void by_operators_for_equal_values()
[TestCase("?", "")]
[TestCase("A++", "?")]
[TestCase("", "?")]
[TestCase("?", "?")]
public void by_operators_unknown_always_false(EnergyLabel l, EnergyLabel r)
{
(l < r).Should().BeFalse();
Expand Down
45 changes: 45 additions & 0 deletions src/Qowaiv/Extensions/System.Span.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#if NET6_0_OR_GREATER
namespace System;

internal static class QowaivSpanExtensions
{
[Pure]
public static bool TryWrite(this Span<char> span, string? value, out int charsWritten)
{
if (value is { Length: > 0 })
{
if (value.TryCopyTo(span))
{
charsWritten = value.Length;
return true;
}
else
{
charsWritten = 0;
return false;
}
}
else
{
charsWritten = 0;
return true;
}
}

[Pure]
public static bool TryWrite(this Span<char> span, char ch, out int charsWritten)
{
if (span.Length != 0)
{
span.Fill(ch);
charsWritten = 1;
return true;
}
else
{
charsWritten = 0;
return false;
}
}
}
#endif
16 changes: 10 additions & 6 deletions src/Qowaiv/Formatting/StringFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,19 +115,23 @@ public static string Apply<T>(T obj, string format, IFormatProvider? formatProvi
/// </returns>
public static bool TryApplyCustomFormatter(string? format, object obj, IFormatProvider? formatProvider, out string formatted)
{
var customFormatter = formatProvider?.GetFormat<ICustomFormatter>();
if (customFormatter is null)
if (formatProvider.TryGetCustomFormatter() is { } customFormatter)
{
formatted = string.Empty;
return false;
formatted = customFormatter.Format(format, obj, formatProvider);
return true;
}
else
{
formatted = customFormatter.Format(format, obj, formatProvider);
return true;
formatted = string.Empty;
return false;
}
}

/// <summary>Returns the <see cref="ICustomFormatter"/> if available.</summary>
[Pure]
public static ICustomFormatter? TryGetCustomFormatter(this IFormatProvider? formatProvider)
=> formatProvider?.GetFormat<ICustomFormatter>();

/// <summary>Replaces diacritic characters by non diacritic ones.</summary>
/// <param name="str">
/// The string to remove the diacritics from.
Expand Down
55 changes: 47 additions & 8 deletions src/Qowaiv/Sustainability/EnergyLabel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,24 @@
#if NET5_0_OR_GREATER
[System.Text.Json.Serialization.JsonConverter(typeof(Json.Sustainability.EnergyLabelJsonConverter))]
#endif
public readonly partial struct EnergyLabel : ISerializable, IXmlSerializable, IFormattable, IEquatable<EnergyLabel>, IComparable, IComparable<EnergyLabel>
public readonly partial struct EnergyLabel : ISerializable, IXmlSerializable, IEquatable<EnergyLabel>, IComparable, IComparable<EnergyLabel>
#if NET6_0_OR_GREATER
, ISpanFormattable
#else
, IFormattable
#endif
{
/// <summary>Represents an empty/not set EU energy label.</summary>
public static readonly EnergyLabel Empty;

private const int MaxPlusses = 4;

/// <summary>Represents EU energy label A/A+/A++/...</summary>
/// <param name="plusses">
/// The total of '+'s [0, 4].
/// </param>
[Pure]
public static EnergyLabel A(int plusses = 0) => plusses < 0 || plusses > 4
public static EnergyLabel A(int plusses = 0) => plusses < 0 || plusses > MaxPlusses
? throw new ArgumentOutOfRangeException(nameof(plusses), QowaivMessages.FormatExceptionEnergyLabel)
: new(7 + plusses);

Expand Down Expand Up @@ -87,8 +94,8 @@ public string ToString(string? format, IFormatProvider? formatProvider)
else if (IsUnknown()) return "?";
else
{
var str = new char[Length(m_Value)];
str[0] = (char)(Char(format) - Delta(m_Value));
var str = new char[StringLength()];
str[0] = Char(format == "l");

for (var i = 1; i < str.Length; i++)
{
Expand All @@ -97,10 +104,42 @@ public string ToString(string? format, IFormatProvider? formatProvider)

return new(str);
}
}

#if NET6_0_OR_GREATER
/// <inheritdoc />
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
{
if (provider.TryGetCustomFormatter() is null)
{
if (IsEmpty()) return destination.TryWrite(string.Empty, out charsWritten);
else if (IsUnknown()) return destination.TryWrite('?', out charsWritten);
else if (StringLength() is { } length && destination.Length >= length)
{
destination[0] = Char(format.Length == 1 && format[0] == 'l');

for (var i = 1; i < length; i++)
{
destination[i] = '+';
}
charsWritten = length;
return true;
}
}
charsWritten = 0;
return false;
}
#endif

static int Length(int value) => value > B.m_Value ? value - B.m_Value : 1;
static int Delta(int value) => Math.Min('H' - 'A', value);
static char Char(string? format) => format == "l" || format == "L" ? 'h' : 'H';
[Pure]
private int StringLength() => m_Value > B.m_Value ? m_Value - B.m_Value : 1;

[Pure]
private char Char(bool toLower)
{
var ch = (char)('H' - m_Value);
ch = ch <= 'A' ? 'A' : ch;
return toLower ? char.ToLowerInvariant(ch) : ch;
}

/// <summary>Gets an XML string representation of the EU energy label.</summary>
Expand Down Expand Up @@ -178,7 +217,7 @@ public static bool TryParse(string? s, IFormatProvider? provider, out EnergyLabe
return false;

static bool IsAPlus(string str)
=> str.Length <= 5
=> str.Length <= MaxPlusses + 1
&& str[0] == 'A'
&& str[1..].All(ch => ch == '+');
}
Expand Down

0 comments on commit 50b3569

Please sign in to comment.