using System.Collections.Concurrent; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using decePubClient.Models.Mastodon; using PrivaPub.ClientModels; namespace decePubClient.Helpers { public sealed class UnregisteredJson : IJsonTypeInfoResolver { public static readonly UnregisteredJson Instance = new(); static readonly DefaultJsonTypeInfoResolver Reflection = new(); static readonly ConcurrentDictionary Reported = new(); public static bool Strict { get; set; } public JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) { if (Strict) throw new NotSupportedException($"{type.FullName} is not registered in a JsonSerializerContext (HR0306)."); if (Reported.TryAdd(type, 0)) Console.Error.WriteLine($"{type.FullName} is serialized without a JsonSerializerContext registration (HR0306)"); return Reflection.GetTypeInfo(type, options); } public static void Check(bool strict) { Strict = strict; if (!strict) return; var missing = Missing(ClientModelsJsonContext.Default, typeof(WebResult), "PrivaPub.ClientModels") .Concat(Missing(MastodonJsonContext.Default, typeof(Status), "decePubClient.Models.Mastodon")) .ToList(); if (missing.Count > 0) throw new InvalidOperationException("Not registered in a JsonSerializerContext (HR0306): " + string.Join(", ", missing)); } static IEnumerable Missing(IJsonTypeInfoResolver context, Type anchor, string space) => anchor.Assembly.GetTypes() .Where(t => t.IsClass && t.IsPublic && !t.IsAbstract && !t.IsGenericTypeDefinition && t.Namespace is { } ns && ns.StartsWith(space, StringComparison.Ordinal) && !ns.EndsWith(".Resources", StringComparison.Ordinal) && !typeof(Attribute).IsAssignableFrom(t) && !typeof(JsonSerializerContext).IsAssignableFrom(t)) .Where(t => context.GetTypeInfo(t, new JsonSerializerOptions()) == null) .Select(t => t.FullName); } }