< Summary

Information
Class: TeleFlow.Telegram.Internal.CallbackDataCodec
Assembly: TeleFlow.Framework
File(s): /_/src/TeleFlow.Framework/Internal/CallbackDataCodec.cs
Line coverage
77%
Covered lines: 34
Uncovered lines: 10
Coverable lines: 44
Total lines: 111
Line coverage: 77.2%
Branch coverage
50%
Covered branches: 13
Total branches: 26
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
PackRequired(...)100%44100%
TryGetGenerated(...)100%11100%
Pack(...)100%11100%
Unpack(...)33.33%251255%
ValidateCallbackData(...)75%4487.5%
IsPayloadDeserializationFailure(...)33.33%66100%

File(s)

/_/src/TeleFlow.Framework/Internal/CallbackDataCodec.cs

#LineLine coverage
 1using System.Text;
 2using System.Text.Json;
 3using TeleFlow.Annotations;
 4
 5namespace TeleFlow.Telegram.Internal;
 6
 7/// <summary>
 8/// Packs and unpacks compact Telegram callback payloads used by typed callback handlers
 9/// and inline keyboard builders before data is sent to or received from Telegram.
 10/// </summary>
 11internal static class CallbackDataCodec
 12{
 13    public const int MaxTelegramCallbackDataBytes = 64;
 14
 15    public static string PackRequired(object payload, Type payloadType)
 16    {
 1017        ArgumentNullException.ThrowIfNull(payload);
 1018        ArgumentNullException.ThrowIfNull(payloadType);
 19
 1020        if (GeneratedCallbackDataCodecRegistry.TryGet(payloadType, out var generatedCodec))
 21        {
 122            return generatedCodec.Pack(payload);
 23        }
 24
 925        if (!CallbackDataMetadata.TryCreate(payloadType, out var metadata))
 26        {
 127            throw new InvalidOperationException(
 128                $"Inline keyboard typed callback payload '{payloadType.FullName}' must be marked with " +
 129                $"{nameof(CallbackDataAttribute)}. Add [CallbackData(\"prefix\")] to the payload type or pass raw string
 30        }
 31
 832        return Pack(payload, metadata);
 33    }
 34
 35    public static bool TryGetGenerated(Type payloadType, out GeneratedCallbackDataCodec codec)
 36    {
 8137        return GeneratedCallbackDataCodecRegistry.TryGet(payloadType, out codec);
 38    }
 39
 40    public static string Pack(object payload, CallbackDataMetadata metadata)
 41    {
 1042        ArgumentNullException.ThrowIfNull(payload);
 1043        ArgumentNullException.ThrowIfNull(metadata);
 44
 1045        var serializedPayload = metadata.Pack(payload);
 1046        ValidateCallbackData(serializedPayload, "Serialized Telegram callback data");
 947        return serializedPayload;
 48    }
 49
 50    public static object Unpack(string serializedPayload, CallbackDataMetadata metadata)
 51    {
 552        ArgumentNullException.ThrowIfNull(serializedPayload);
 553        ArgumentNullException.ThrowIfNull(metadata);
 54
 555        var parts = serializedPayload.Split(':');
 56
 557        if (parts.Length == 0 ||
 558            !string.Equals(parts[0], metadata.Prefix, StringComparison.Ordinal))
 59        {
 060            throw new JsonException(
 061                $"Telegram callback data prefix does not match payload type '{metadata.PayloadType.FullName}'.");
 62        }
 63
 564        if (parts.Length - 1 != metadata.Fields.Count)
 65        {
 066            throw new JsonException(
 067                $"Telegram callback data field count does not match payload type '{metadata.PayloadType.FullName}'.");
 68        }
 69
 570        var values = metadata.Fields
 1071            .Select((field, index) => metadata.ParseField(parts[index + 1], field.Property.PropertyType))
 572            .ToArray();
 73
 474        if (metadata.Constructor is not null)
 75        {
 476            return metadata.Constructor.Invoke(values);
 77        }
 78
 079        var payload = Activator.CreateInstance(metadata.PayloadType)
 080            ?? throw new JsonException($"Unable to create callback data payload type '{metadata.PayloadType.FullName}'."
 81
 082        for (var index = 0; index < metadata.Fields.Count; index++)
 83        {
 084            metadata.Fields[index].Property.SetValue(payload, values[index]);
 85        }
 86
 087        return payload;
 88    }
 89
 90    public static void ValidateCallbackData(string callbackData, string subject)
 91    {
 2392        ArgumentNullException.ThrowIfNull(callbackData);
 2393        ArgumentException.ThrowIfNullOrWhiteSpace(subject);
 94
 2395        if (string.IsNullOrWhiteSpace(callbackData))
 96        {
 097            throw new InvalidOperationException($"{subject} must not be empty.");
 98        }
 99
 23100        if (Encoding.UTF8.GetByteCount(callbackData) > MaxTelegramCallbackDataBytes)
 101        {
 3102            throw new InvalidOperationException(
 3103                $"{subject} must be at most {MaxTelegramCallbackDataBytes} UTF-8 bytes.");
 104        }
 20105    }
 106
 107    public static bool IsPayloadDeserializationFailure(Exception exception)
 108    {
 2109        return exception is JsonException or FormatException or OverflowException;
 110    }
 111}