using Microsoft.AspNetCore.Http.Features; using System.Text.Json; namespace PrivaPub.Api.Mastodon.Infrastructure { public sealed class MastodonParams { readonly Dictionary> _values; MastodonParams(Dictionary> values) { _values = values; } public static async Task Read(HttpRequest request, CancellationToken token) { var values = new Dictionary>(StringComparer.Ordinal); foreach (var pair in request.Query) foreach (var value in pair.Value) Add(values, pair.Key, value); if (request.HasFormContentType) { var form = await request.ReadFormAsync(token); foreach (var pair in form) foreach (var value in pair.Value) Add(values, pair.Key, value); } else if (request.ContentType?.Contains("json", StringComparison.OrdinalIgnoreCase) == true && request.ContentLength != 0) { try { using var document = await JsonDocument.ParseAsync(request.Body, cancellationToken: token); Flatten(values, default, document.RootElement); } catch (JsonException) { } } return new MastodonParams(values); } public static MastodonParams From(IEnumerable> pairs) { var values = new Dictionary>(StringComparer.Ordinal); foreach (var pair in pairs) Add(values, pair.Key, pair.Value); return new MastodonParams(values); } public bool Has(string name) => _values.ContainsKey(name); public string Get(string name) => _values.TryGetValue(name, out var list) && list.Count > 0 ? list[^1] : default; public IReadOnlyList List(string name) => _values.TryGetValue(name, out var list) ? list : (IReadOnlyList)Array.Empty(); public bool? Bool(string name) => Get(name)?.ToLowerInvariant() switch { "true" or "1" or "on" or "yes" => true, "false" or "0" or "off" or "no" or "" => false, _ => (bool?)null }; public int? Int(string name) => int.TryParse(Get(name), out var value) ? value : (int?)null; static void Add(Dictionary> values, string key, string value) { key = key.EndsWith("[]", StringComparison.Ordinal) ? key[..^2] : key; if (!values.TryGetValue(key, out var list)) values[key] = list = new List(); list.Add(value); } static void Flatten(Dictionary> values, string prefix, JsonElement element) { switch (element.ValueKind) { case JsonValueKind.Object: foreach (var property in element.EnumerateObject()) Flatten(values, prefix == default ? property.Name : $"{prefix}[{property.Name}]", property.Value); break; case JsonValueKind.Array: foreach (var item in element.EnumerateArray()) { if (item.ValueKind is JsonValueKind.Object or JsonValueKind.Array) Flatten(values, prefix + "[]", item); else Add(values, prefix, Scalar(item)); } break; case JsonValueKind.Null or JsonValueKind.Undefined: break; default: if (prefix != default) Add(values, prefix, Scalar(element)); break; } } static string Scalar(JsonElement element) => element.ValueKind switch { JsonValueKind.String => element.GetString(), JsonValueKind.True => "true", JsonValueKind.False => "false", _ => element.GetRawText() }; } }