// SPDX-License-Identifier: AGPL-3.0-or-later // Copyright (C) 2022-2026 Aykut Alparslan KOC using System.Collections.ObjectModel; namespace Ferrite.TL.Schema; public enum LayerWireKind { Int32, Int64, Double, Int128, Int256, Int512, Bytes, String, Flags, True, Object, Vector } public sealed class LayerType { public LayerWireKind Kind { get; } public string? Name { get; } public bool IsBare { get; } public LayerType? ElementType { get; } public LayerType(LayerWireKind kind, string? name = null, bool isBare = false, LayerType? elementType = null) { Kind = kind; Name = name; IsBare = isBare; ElementType = elementType; } public bool HasSameWireType(LayerType other) { if (Kind != other.Kind || IsBare != other.IsBare || !string.Equals(Name, other.Name, StringComparison.Ordinal)) { return false; } if (ElementType == null || other.ElementType == null) { return ElementType == null && other.ElementType == null; } return ElementType.HasSameWireType(other.ElementType); } } public sealed class LayerField { public string Name { get; } public LayerType Type { get; } public string? FlagsField { get; } public int? FlagBit { get; } public bool IsConditional => FlagsField != null && FlagBit != null; public LayerField(string name, LayerType type, string? flagsField = null, int? flagBit = null) { Name = name; Type = type; FlagsField = flagsField; FlagBit = flagBit; } } public sealed class LayerConstructor { private readonly IReadOnlyList _fields; public string Name { get; } public int Id { get; } public string ResultType { get; } public IReadOnlyList Fields => _fields; public LayerConstructor(string name, int id, string resultType, IEnumerable fields) { Name = name; Id = id; ResultType = resultType; _fields = Array.AsReadOnly(fields.ToArray()); } public LayerField? FindField(string name) { for (int i = 0; i < _fields.Count; i++) { if (string.Equals(_fields[i].Name, name, StringComparison.Ordinal)) { return _fields[i]; } } return null; } public bool HasSameShape(LayerConstructor other) { if (Id != other.Id || !string.Equals(Name, other.Name, StringComparison.Ordinal) || !string.Equals(ResultType, other.ResultType, StringComparison.Ordinal) || _fields.Count != other._fields.Count) { return false; } for (int i = 0; i < _fields.Count; i++) { LayerField left = _fields[i]; LayerField right = other._fields[i]; if (!string.Equals(left.Name, right.Name, StringComparison.Ordinal) || !left.Type.HasSameWireType(right.Type) || !string.Equals(left.FlagsField, right.FlagsField, StringComparison.Ordinal) || left.FlagBit != right.FlagBit) { return false; } } return true; } } public sealed class LayerSchema { private readonly IReadOnlyList _constructors; private readonly IReadOnlyDictionary _byId; private readonly IReadOnlyDictionary _byName; private readonly IReadOnlyDictionary> _byResultType; private readonly IReadOnlyDictionary _vectorResults; private readonly IReadOnlyList _methods; private readonly IReadOnlyDictionary _methodsById; private readonly IReadOnlyDictionary _methodsByName; public int Layer { get; } public IReadOnlyList Constructors => _constructors; public IReadOnlyList Methods => _methods; public IReadOnlyDictionary VectorResults => _vectorResults; public LayerSchema(int layer, IEnumerable constructors, IEnumerable>? vectorResults = null, IEnumerable? methods = null) { Layer = layer; LayerConstructor[] values = constructors.ToArray(); var byId = new Dictionary(); var byName = new Dictionary(StringComparer.Ordinal); var byResult = new Dictionary>(StringComparer.Ordinal); foreach (LayerConstructor constructor in values) { if (byId.ContainsKey(constructor.Id)) { throw new ArgumentException("Duplicate constructor id.", nameof(constructors)); } if (byName.ContainsKey(constructor.Name)) { throw new ArgumentException("Duplicate constructor name.", nameof(constructors)); } byId.Add(constructor.Id, constructor); byName.Add(constructor.Name, constructor); if (!byResult.TryGetValue(constructor.ResultType, out List? result)) { result = new List(); byResult.Add(constructor.ResultType, result); } result.Add(constructor); } var readOnlyResult = new Dictionary>( StringComparer.Ordinal); foreach (KeyValuePair> pair in byResult) { readOnlyResult.Add(pair.Key, Array.AsReadOnly(pair.Value.ToArray())); } _constructors = Array.AsReadOnly(values); _byId = new ReadOnlyDictionary(byId); _byName = new ReadOnlyDictionary(byName); _byResultType = new ReadOnlyDictionary>( readOnlyResult); var vectors = new Dictionary(); if (vectorResults != null) { foreach (KeyValuePair entry in vectorResults) { vectors.Add(entry.Key, entry.Value); } } _vectorResults = new ReadOnlyDictionary(vectors); LayerConstructor[] methodValues = methods == null ? Array.Empty() : methods.ToArray(); var methodsById = new Dictionary(); var methodsByName = new Dictionary(StringComparer.Ordinal); foreach (LayerConstructor method in methodValues) { if (methodsById.ContainsKey(method.Id)) { throw new ArgumentException("Duplicate method id.", nameof(methods)); } if (methodsByName.ContainsKey(method.Name)) { throw new ArgumentException("Duplicate method name.", nameof(methods)); } methodsById.Add(method.Id, method); methodsByName.Add(method.Name, method); } _methods = Array.AsReadOnly(methodValues); _methodsById = new ReadOnlyDictionary(methodsById); _methodsByName = new ReadOnlyDictionary(methodsByName); } public bool TryGetMethod(int id, out LayerConstructor method) { return _methodsById.TryGetValue(id, out method!); } public bool TryGetMethod(string name, out LayerConstructor method) { return _methodsByName.TryGetValue(name, out method!); } public LayerConstructor GetMethod(string name) { if (!_methodsByName.TryGetValue(name, out LayerConstructor? method)) { throw new ArgumentException("Unknown method.", nameof(name)); } return method; } public bool TryGetVectorResultElement(int methodId, out string elementType) { return _vectorResults.TryGetValue(methodId, out elementType!); } public bool TryGetConstructor(int id, out LayerConstructor constructor) { return _byId.TryGetValue(id, out constructor!); } public bool TryGetConstructor(string name, out LayerConstructor constructor) { return _byName.TryGetValue(name, out constructor!); } public LayerConstructor GetConstructor(string name) { if (!_byName.TryGetValue(name, out LayerConstructor? constructor)) { throw new ArgumentException("Unknown constructor.", nameof(name)); } return constructor; } public bool TryGetBareConstructor(string resultType, out LayerConstructor constructor) { constructor = null!; return _byResultType.TryGetValue(resultType, out IReadOnlyList? values) && values.Count == 1 && (constructor = values[0]) != null; } } public sealed class LayerObjectValue { private readonly IReadOnlyDictionary _fields; public LayerConstructor Constructor { get; } public IReadOnlyDictionary Fields => _fields; public LayerObjectValue(LayerConstructor constructor, IEnumerable> fields) { Constructor = constructor; var values = new Dictionary(StringComparer.Ordinal); foreach (KeyValuePair field in fields) { values.Add(field.Key, field.Value); } _fields = new ReadOnlyDictionary(values); } public LayerObjectValue(LayerConstructor constructor, IReadOnlyDictionary fields) : this(constructor, fields.Select(x => x)) { } public bool TryGetValue(string name, out object? value) { return _fields.TryGetValue(name, out value); } public T Get(string name) { if (!TryGetValue(name, out object? value) || value is not T typed) { throw new InvalidOperationException("The field " + name + " is absent."); } return typed; } }