< Summary

Information
Class: TeleFlow.Telegram.I18n.Fluent.Internal.FluentValueConverter
Assembly: TeleFlow.Framework.I18n.Fluent
File(s): /_/src/TeleFlow.Framework.I18n.Fluent/Internal/FluentValues.cs
Line coverage
55%
Covered lines: 24
Uncovered lines: 19
Coverable lines: 43
Total lines: 183
Line coverage: 55.8%
Branch coverage
55%
Covered branches: 30
Total branches: 54
Branch coverage: 55.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Convert(...)54.54%4284441.66%
Escape(...)75%4485.71%
ConvertFormattedText(...)50%7666.66%

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    {
 51617        return value switch
 51618        {
 019            null => (FluentString)string.Empty,
 220            TelegramFormattedText formattedText => ConvertFormattedText(formattedText, mode),
 021            DateTime dateTime => new FluentDateTimeValue(dateTime, culture, mode),
 522            DateTimeOffset dateTimeOffset => new FluentDateTimeValue(dateTimeOffset, culture, mode),
 323            DateOnly date => new FluentDateTimeValue(date, culture, mode),
 124            TimeOnly time => new FluentDateTimeValue(time, culture, mode),
 025            byte number => (FluentNumber)(double)number,
 026            sbyte number => (FluentNumber)(double)number,
 027            short number => (FluentNumber)(double)number,
 028            ushort number => (FluentNumber)(double)number,
 49229            int number => (FluentNumber)(double)number,
 030            uint number => (FluentNumber)(double)number,
 031            long number => (FluentNumber)number,
 032            ulong number => (FluentNumber)number,
 033            float number => (FluentNumber)number,
 134            double number => (FluentNumber)number,
 035            decimal number => (FluentNumber)(double)number,
 1236            string text => (FluentString)Escape(text, mode),
 037            char character => (FluentString)Escape(character.ToString(), mode),
 038            IFormattable formattable => (FluentString)Escape(formattable.ToString(null, culture) ?? string.Empty, mode),
 039            _ => (FluentString)Escape(value.ToString() ?? string.Empty, mode)
 51640        };
 41    }
 42
 43    public static string Escape(string value, FluentRenderingMode mode)
 44    {
 2345        return mode switch
 2346        {
 1747            FluentRenderingMode.Plain => value,
 448            FluentRenderingMode.Html => TelegramHtml.Escape(value),
 249            FluentRenderingMode.MarkdownV2 => TelegramMarkdownV2.Escape(value),
 050            _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unknown Fluent rendering mode.")
 2351        };
 52    }
 53
 54    private static IFluentType ConvertFormattedText(
 55        TelegramFormattedText formattedText,
 56        FluentRenderingMode mode)
 57    {
 258        var expectedParseMode = mode switch
 259        {
 260            FluentRenderingMode.Html => TelegramParseMode.Html,
 061            FluentRenderingMode.MarkdownV2 => TelegramParseMode.MarkdownV2,
 062            FluentRenderingMode.Plain => throw new InvalidOperationException(
 063                "TelegramFormattedText cannot be inserted into a plain Fluent message."),
 064            _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unknown Fluent rendering mode.")
 265        };
 66
 267        if (formattedText.ParseMode != expectedParseMode)
 68        {
 169            throw new InvalidOperationException(
 170                $"TelegramFormattedText with parse mode '{formattedText.ParseMode}' cannot be inserted into '{expectedPa
 71        }
 72
 173        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{
 107    private readonly IFormattable _value = value;
 108
 109    public string AsString()
 110    {
 111        return FluentValueConverter.Escape(Format(culture, null, null), mode);
 112    }
 113
 114    public bool IsError() => false;
 115
 116    public bool Matches(IFluentType other, IScope scope)
 117    {
 118        ArgumentNullException.ThrowIfNull(other);
 119        ArgumentNullException.ThrowIfNull(scope);
 120        return other is FluentDateTimeValue dateTime && Equals(_value, dateTime._value);
 121    }
 122
 123    public IFluentType Copy() => this;
 124
 125    public string Format(CultureInfo targetCulture, string? dateStyle, string? timeStyle)
 126    {
 127        var normalizedDateStyle = NormalizeStyle(dateStyle, "dateStyle");
 128        var normalizedTimeStyle = NormalizeStyle(timeStyle, "timeStyle");
 129
 130        if (_value is DateOnly date)
 131        {
 132            if (normalizedTimeStyle is not null)
 133            {
 134                throw new InvalidOperationException("DATETIME cannot apply timeStyle to a DateOnly value.");
 135            }
 136
 137            return date.ToString(normalizedDateStyle == "long" ? "D" : "d", targetCulture);
 138        }
 139
 140        if (_value is TimeOnly time)
 141        {
 142            if (normalizedDateStyle is not null)
 143            {
 144                throw new InvalidOperationException("DATETIME cannot apply dateStyle to a TimeOnly value.");
 145            }
 146
 147            return time.ToString(normalizedTimeStyle == "long" ? "T" : "t", targetCulture);
 148        }
 149
 150        var format = (normalizedDateStyle, normalizedTimeStyle) switch
 151        {
 152            (null, null) => "G",
 153            ("short", null) => "d",
 154            ("long", null) => "D",
 155            (null, "short") => "t",
 156            (null, "long") => "T",
 157            ("short", "short") => "g",
 158            ("short", "long") => "G",
 159            ("long", "short") => "f",
 160            ("long", "long") => "F",
 161            _ => throw new InvalidOperationException("DATETIME could not select a date or time format.")
 162        };
 163
 164        return _value.ToString(format, targetCulture);
 165    }
 166
 167    private static string? NormalizeStyle(string? style, string optionName)
 168    {
 169        if (style is null)
 170        {
 171            return null;
 172        }
 173
 174        var normalized = style.ToLowerInvariant();
 175
 176        return normalized switch
 177        {
 178            "short" or "long" => normalized,
 179            _ => throw new InvalidOperationException(
 180                $"DATETIME option '{optionName}' must be short or long.")
 181        };
 182    }
 183}