< Summary

Information
Class: TeleFlow.Telegram.I18n.Fluent.Internal.FluentDateTimeValue
Assembly: TeleFlow.Framework.I18n.Fluent
File(s): /_/src/TeleFlow.Framework.I18n.Fluent/Internal/FluentValues.cs
Line coverage
67%
Covered lines: 27
Uncovered lines: 13
Coverable lines: 40
Total lines: 183
Line coverage: 67.5%
Branch coverage
59%
Covered branches: 26
Total branches: 44
Branch coverage: 59%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
AsString()100%210%
IsError()100%210%
Matches(...)0%620%
Copy()100%11100%
Format(...)55.55%843666.66%
NormalizeStyle(...)100%66100%

File(s)

/_/src/TeleFlow.Framework.I18n.Fluent/Internal/FluentValues.cs

#LineLine coverage
 1using System.Globalization;
 2using Linguini.Shared.Types.Bundle;
 3using TeleFlow.Telegram.Formatting;
 4
 5namespace TeleFlow.Telegram.I18n.Fluent.Internal;
 6
 7/// <summary>
 8/// Converts application arguments into Linguini values while applying the selected Telegram escaping boundary exactly o
 9/// </summary>
 10internal static class FluentValueConverter
 11{
 12    public static IFluentType Convert(
 13        object? value,
 14        CultureInfo culture,
 15        FluentRenderingMode mode)
 16    {
 17        return value switch
 18        {
 19            null => (FluentString)string.Empty,
 20            TelegramFormattedText formattedText => ConvertFormattedText(formattedText, mode),
 21            DateTime dateTime => new FluentDateTimeValue(dateTime, culture, mode),
 22            DateTimeOffset dateTimeOffset => new FluentDateTimeValue(dateTimeOffset, culture, mode),
 23            DateOnly date => new FluentDateTimeValue(date, culture, mode),
 24            TimeOnly time => new FluentDateTimeValue(time, culture, mode),
 25            byte number => (FluentNumber)(double)number,
 26            sbyte number => (FluentNumber)(double)number,
 27            short number => (FluentNumber)(double)number,
 28            ushort number => (FluentNumber)(double)number,
 29            int number => (FluentNumber)(double)number,
 30            uint number => (FluentNumber)(double)number,
 31            long number => (FluentNumber)number,
 32            ulong number => (FluentNumber)number,
 33            float number => (FluentNumber)number,
 34            double number => (FluentNumber)number,
 35            decimal number => (FluentNumber)(double)number,
 36            string text => (FluentString)Escape(text, mode),
 37            char character => (FluentString)Escape(character.ToString(), mode),
 38            IFormattable formattable => (FluentString)Escape(formattable.ToString(null, culture) ?? string.Empty, mode),
 39            _ => (FluentString)Escape(value.ToString() ?? string.Empty, mode)
 40        };
 41    }
 42
 43    public static string Escape(string value, FluentRenderingMode mode)
 44    {
 45        return mode switch
 46        {
 47            FluentRenderingMode.Plain => value,
 48            FluentRenderingMode.Html => TelegramHtml.Escape(value),
 49            FluentRenderingMode.MarkdownV2 => TelegramMarkdownV2.Escape(value),
 50            _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unknown Fluent rendering mode.")
 51        };
 52    }
 53
 54    private static IFluentType ConvertFormattedText(
 55        TelegramFormattedText formattedText,
 56        FluentRenderingMode mode)
 57    {
 58        var expectedParseMode = mode switch
 59        {
 60            FluentRenderingMode.Html => TelegramParseMode.Html,
 61            FluentRenderingMode.MarkdownV2 => TelegramParseMode.MarkdownV2,
 62            FluentRenderingMode.Plain => throw new InvalidOperationException(
 63                "TelegramFormattedText cannot be inserted into a plain Fluent message."),
 64            _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unknown Fluent rendering mode.")
 65        };
 66
 67        if (formattedText.ParseMode != expectedParseMode)
 68        {
 69            throw new InvalidOperationException(
 70                $"TelegramFormattedText with parse mode '{formattedText.ParseMode}' cannot be inserted into '{expectedPa
 71        }
 72
 73        return new TrustedFormattedFluentValue(formattedText.Text);
 74    }
 75}
 76
 77/// <summary>
 78/// Carries a reviewed formatted Telegram fragment through Fluent placeable resolution without escaping it again.
 79/// </summary>
 80internal sealed class TrustedFormattedFluentValue(string text) : IFluentType
 81{
 82    private readonly string _text = text;
 83
 84    public string AsString() => _text;
 85
 86    public bool IsError() => false;
 87
 88    public bool Matches(IFluentType other, IScope scope)
 89    {
 90        ArgumentNullException.ThrowIfNull(other);
 91        ArgumentNullException.ThrowIfNull(scope);
 92        return other is TrustedFormattedFluentValue formatted &&
 93               string.Equals(_text, formatted._text, StringComparison.Ordinal);
 94    }
 95
 96    public IFluentType Copy() => this;
 97}
 98
 99/// <summary>
 100/// Carries a .NET temporal value through Fluent so DATETIME can apply options and direct placeables remain locale-aware
 101/// </summary>
 102internal sealed class FluentDateTimeValue(
 103    IFormattable value,
 104    CultureInfo culture,
 105    FluentRenderingMode mode) : IFluentType
 106{
 9107    private readonly IFormattable _value = value;
 108
 109    public string AsString()
 110    {
 0111        return FluentValueConverter.Escape(Format(culture, null, null), mode);
 112    }
 113
 0114    public bool IsError() => false;
 115
 116    public bool Matches(IFluentType other, IScope scope)
 117    {
 0118        ArgumentNullException.ThrowIfNull(other);
 0119        ArgumentNullException.ThrowIfNull(scope);
 0120        return other is FluentDateTimeValue dateTime && Equals(_value, dateTime._value);
 121    }
 122
 9123    public IFluentType Copy() => this;
 124
 125    public string Format(CultureInfo targetCulture, string? dateStyle, string? timeStyle)
 126    {
 9127        var normalizedDateStyle = NormalizeStyle(dateStyle, "dateStyle");
 7128        var normalizedTimeStyle = NormalizeStyle(timeStyle, "timeStyle");
 129
 7130        if (_value is DateOnly date)
 131        {
 1132            if (normalizedTimeStyle is not null)
 133            {
 0134                throw new InvalidOperationException("DATETIME cannot apply timeStyle to a DateOnly value.");
 135            }
 136
 1137            return date.ToString(normalizedDateStyle == "long" ? "D" : "d", targetCulture);
 138        }
 139
 6140        if (_value is TimeOnly time)
 141        {
 1142            if (normalizedDateStyle is not null)
 143            {
 0144                throw new InvalidOperationException("DATETIME cannot apply dateStyle to a TimeOnly value.");
 145            }
 146
 1147            return time.ToString(normalizedTimeStyle == "long" ? "T" : "t", targetCulture);
 148        }
 149
 5150        var format = (normalizedDateStyle, normalizedTimeStyle) switch
 5151        {
 0152            (null, null) => "G",
 0153            ("short", null) => "d",
 0154            ("long", null) => "D",
 0155            (null, "short") => "t",
 0156            (null, "long") => "T",
 2157            ("short", "short") => "g",
 1158            ("short", "long") => "G",
 1159            ("long", "short") => "f",
 1160            ("long", "long") => "F",
 0161            _ => throw new InvalidOperationException("DATETIME could not select a date or time format.")
 5162        };
 163
 5164        return _value.ToString(format, targetCulture);
 165    }
 166
 167    private static string? NormalizeStyle(string? style, string optionName)
 168    {
 16169        if (style is null)
 170        {
 4171            return null;
 172        }
 173
 12174        var normalized = style.ToLowerInvariant();
 175
 12176        return normalized switch
 12177        {
 10178            "short" or "long" => normalized,
 2179            _ => throw new InvalidOperationException(
 2180                $"DATETIME option '{optionName}' must be short or long.")
 12181        };
 182    }
 183}