Ferrite.TL.Schema/LayerTransformEdgeDefinition.cs
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2022-2026 Aykut Alparslan KOC
using System.Collections.ObjectModel;
namespace Ferrite.TL.Schema;
public sealed class LayerTransformEdgeDefinition : ILayerTransformEdge
{
private readonly IReadOnlyDictionary<string, LayerConstructorDisposition> _dispositions;
private readonly IReadOnlyDictionary<string, LayerSemanticConverter> _converters;
private readonly IReadOnlyList<LayerConstructorDisposition> _dispositionValues;
private readonly IReadOnlyDictionary<string, LayerType> _scalarUnwraps;
public LayerEdge Edge { get; }
public LayerSchema SourceSchema { get; }
public LayerSchema TargetSchema { get; }
public bool HasUnsupportedDispositions { get; }
public IReadOnlyList<LayerConstructorDisposition> Dispositions => _dispositionValues;
public LayerTransformEdgeDefinition(LayerSchema sourceSchema,
LayerSchema targetSchema,
IEnumerable<LayerConstructorDisposition> dispositions,
IReadOnlyDictionary<string, LayerSemanticConverter>? converters = null)
{
if (sourceSchema.Layer == targetSchema.Layer)
{
throw new ArgumentException("An edge must join different layers.");
}
SourceSchema = sourceSchema;
TargetSchema = targetSchema;
Edge = new LayerEdge(sourceSchema.Layer, targetSchema.Layer);
LayerConstructorDisposition[] values = dispositions.ToArray();
var byConstructor = new Dictionary<string, LayerConstructorDisposition>(StringComparer.Ordinal);
foreach (LayerConstructorDisposition disposition in values)
{
if (byConstructor.ContainsKey(disposition.SourceConstructor))
{
throw new ArgumentException("Duplicate constructor disposition.",
nameof(dispositions));
}
byConstructor.Add(disposition.SourceConstructor, disposition);
}
_dispositions = new ReadOnlyDictionary<string, LayerConstructorDisposition>(byConstructor);
_dispositionValues = Array.AsReadOnly(values);
var converterValues = new Dictionary<string, LayerSemanticConverter>(
StringComparer.Ordinal);
if (converters != null)
{
foreach (KeyValuePair<string, LayerSemanticConverter> converter in converters)
{
converterValues.Add(converter.Key, converter.Value);
}
}
_converters = new ReadOnlyDictionary<string, LayerSemanticConverter>(
converterValues);
ValidateCoverage();
_scalarUnwraps = ScalarUnwraps();
HasUnsupportedDispositions = values.Any(x =>
x.Kind == LayerDispositionKind.Unsupported);
}
public LayerTransformResult Transform(byte[] source)
{
return Transform(source, null);
}
public LayerTransformResult Transform(byte[] source, string? vectorElementType)
{
int? constructor = source.Length >= 4
? source[0] | source[1] << 8 | source[2] << 16 | source[3] << 24
: (int?)null;
try
{
var reader = new LayerWireReader(source);
var writer = new LayerWireWriter();
if (vectorElementType != null)
{
LayerType rootType = VectorResultType(vectorElementType);
object rootValue = LayerWireCodec.ReadValue(reader, SourceSchema, rootType);
if (!reader.IsComplete)
{
throw new LayerWireException(
"Trailing bytes remain after the root vector.");
}
if (_scalarUnwraps.TryGetValue(vectorElementType, out LayerType? scalarType))
{
LayerWireCodec.WriteValue(writer, TargetSchema,
new LayerType(LayerWireKind.Vector, null, false, scalarType),
UnwrapScalars((IReadOnlyList<object?>)rootValue));
return LayerTransformResult.TransformedVector(writer.ToArray(),
VectorElementName(scalarType));
}
LayerWireCodec.WriteValue(writer, TargetSchema, rootType,
TransformValue(rootType, rootType, rootValue));
byte[] rootBytes = writer.ToArray();
return source.SequenceEqual(rootBytes)
? LayerTransformResult.Identity(source)
: LayerTransformResult.Transformed(rootBytes);
}
LayerObjectValue sourceValue = LayerWireCodec.ReadBoxedObject(reader, SourceSchema);
if (!reader.IsComplete)
{
throw new LayerWireException("Trailing bytes remain after the root object.");
}
if (TryUnwrapVector(sourceValue, out LayerType vectorType, out object? vector))
{
LayerWireCodec.WriteValue(writer, TargetSchema, vectorType,
TransformValue(vectorType, vectorType, vector));
return LayerTransformResult.TransformedVector(writer.ToArray(),
VectorElementName(vectorType.ElementType!));
}
LayerObjectValue targetValue = TransformObject(sourceValue);
LayerWireCodec.WriteBoxedObject(writer, TargetSchema, targetValue);
byte[] transformed = writer.ToArray();
if (source.SequenceEqual(transformed))
{
return LayerTransformResult.Identity(source);
}
return LayerTransformResult.Transformed(transformed);
}
catch (LayerUncoveredException exception)
{
return LayerTransformResult.Failure(LayerTransformStatus.Uncovered,
Edge, constructor, exception.Message);
}
catch (LayerWireException exception)
{
return LayerTransformResult.Failure(LayerTransformStatus.Malformed,
Edge, constructor, exception.Message);
}
catch (Exception exception)
{
return LayerTransformResult.Failure(LayerTransformStatus.Malformed,
Edge, constructor, exception.Message);
}
}
private static LayerType VectorResultType(string elementType)
{
return new LayerType(LayerWireKind.Vector, null, false,
VectorElementType(elementType));
}
private static LayerType VectorElementType(string elementType)
{
switch (elementType)
{
case "int":
return new LayerType(LayerWireKind.Int32);
case "long":
return new LayerType(LayerWireKind.Int64);
case "double":
return new LayerType(LayerWireKind.Double);
case "string":
return new LayerType(LayerWireKind.String);
case "bytes":
return new LayerType(LayerWireKind.Bytes);
default:
return new LayerType(LayerWireKind.Object, elementType);
}
}
private static string VectorElementName(LayerType elementType)
{
switch (elementType.Kind)
{
case LayerWireKind.Int32:
return "int";
case LayerWireKind.Int64:
return "long";
case LayerWireKind.Double:
return "double";
case LayerWireKind.String:
return "string";
case LayerWireKind.Bytes:
return "bytes";
default:
return elementType.Name ??
throw new LayerUncoveredException("A vector element type has no name.");
}
}
private bool TryUnwrapVector(LayerObjectValue source, out LayerType vectorType,
out object? vector)
{
vectorType = null!;
vector = null;
if (!_dispositions.TryGetValue(source.Constructor.Name,
out LayerConstructorDisposition? disposition) ||
disposition.Kind != LayerDispositionKind.Unwrap)
{
return false;
}
LayerField field = UnwrappedField(source, disposition, out object? value);
if (field.Type.Kind != LayerWireKind.Vector)
{
return false;
}
vectorType = field.Type;
vector = value;
return true;
}
private IReadOnlyList<object?> UnwrapScalars(IReadOnlyList<object?> values)
{
var scalars = new object?[values.Count];
for (int index = 0; index < values.Count; index++)
{
var value = (LayerObjectValue)values[index]!;
UnwrappedField(value, _dispositions[value.Constructor.Name], out scalars[index]);
}
return Array.AsReadOnly(scalars);
}
private IReadOnlyDictionary<string, LayerType> ScalarUnwraps()
{
var scalars = new Dictionary<string, LayerType>(StringComparer.Ordinal);
foreach (LayerConstructorDisposition disposition in _dispositionValues)
{
LayerConstructor source = SourceSchema.GetConstructor(disposition.SourceConstructor);
LayerType? type = disposition.Kind == LayerDispositionKind.Unwrap
? source.FindField(disposition.Field!)!.Type
: null;
if (type == null || !IsScalar(type))
{
continue;
}
foreach (LayerConstructor sibling in SourceSchema.Constructors)
{
if (sibling.ResultType != source.ResultType)
{
continue;
}
if (!_dispositions.TryGetValue(sibling.Name,
out LayerConstructorDisposition? unwrap) ||
unwrap.Kind != LayerDispositionKind.Unwrap ||
!sibling.FindField(unwrap.Field!)!.Type.HasSameWireType(type))
{
throw new ArgumentException("Every constructor of " + source.ResultType +
" must unwrap the same scalar.");
}
}
scalars[source.ResultType] = type;
}
return new ReadOnlyDictionary<string, LayerType>(scalars);
}
private static bool IsScalar(LayerType type)
{
return type.Kind is LayerWireKind.Int32 or LayerWireKind.Int64
or LayerWireKind.Double or LayerWireKind.String or LayerWireKind.Bytes;
}
private static LayerField UnwrappedField(LayerObjectValue source,
LayerConstructorDisposition disposition, out object? value)
{
LayerField field = source.Constructor.FindField(disposition.Field!)!;
if (!source.TryGetValue(field.Name, out value) || value == null)
{
throw new LayerUncoveredException("Unwrapped field is absent: " +
source.Constructor.Name + "." + field.Name + ".");
}
return field;
}
private void ValidateCoverage()
{
var changed = new HashSet<string>(StringComparer.Ordinal);
foreach (LayerConstructor source in SourceSchema.Constructors)
{
if (!TargetSchema.TryGetConstructor(source.Name, out LayerConstructor target) ||
!source.HasSameShape(target))
{
changed.Add(source.Name);
}
}
foreach (string name in changed)
{
if (!_dispositions.ContainsKey(name))
{
throw new ArgumentException("Missing constructor disposition for " + name + ".");
}
}
foreach (LayerConstructorDisposition disposition in _dispositionValues)
{
if (!changed.Contains(disposition.SourceConstructor))
{
throw new ArgumentException("Disposition does not match a schema change for " +
disposition.SourceConstructor + ".");
}
if (!SourceSchema.TryGetConstructor(disposition.SourceConstructor, out LayerConstructor source))
{
throw new ArgumentException("Disposition names an unknown source constructor.");
}
if (disposition.Kind == LayerDispositionKind.Mechanical)
{
string targetName = disposition.TargetConstructor ?? source.Name;
if (!TargetSchema.TryGetConstructor(targetName, out LayerConstructor target))
{
throw new ArgumentException("Mechanical target constructor is missing.");
}
ValidateMechanicalMap(source, target);
}
else if (disposition.Kind == LayerDispositionKind.Semantic)
{
if (string.IsNullOrWhiteSpace(disposition.ConverterName))
{
throw new ArgumentException("A semantic disposition requires a converter name.");
}
}
else if (disposition.Kind == LayerDispositionKind.Unwrap)
{
LayerField? field = disposition.Field == null
? null
: source.FindField(disposition.Field);
if (field == null ||
field.Type.Kind is not (LayerWireKind.Object or LayerWireKind.Vector) &&
!IsScalar(field.Type))
{
throw new ArgumentException("An unwrap disposition must name an object, vector or scalar field of " +
source.Name + ".");
}
}
}
}
private static void ValidateMechanicalMap(LayerConstructor source,
LayerConstructor target)
{
foreach (LayerField targetField in target.Fields)
{
if (targetField.Type.Kind == LayerWireKind.Flags)
{
continue;
}
LayerField? sourceField = source.FindField(targetField.Name);
if (sourceField == null ||
!sourceField.Type.HasSameWireType(targetField.Type) ||
sourceField.IsConditional != targetField.IsConditional)
{
throw new ArgumentException("Mechanical field map is not wire-compatible for " +
source.Name + "." + targetField.Name + ".");
}
}
}
private LayerObjectValue TransformObject(LayerObjectValue source)
{
if (_dispositions.TryGetValue(source.Constructor.Name,
out LayerConstructorDisposition? disposition))
{
switch (disposition.Kind)
{
case LayerDispositionKind.Mechanical:
return TransformMechanical(source,
disposition.TargetConstructor ?? source.Constructor.Name);
case LayerDispositionKind.Semantic:
if (!_converters.TryGetValue(disposition.ConverterName!,
out LayerSemanticConverter? converter))
{
throw new LayerUncoveredException("Missing semantic converter " +
disposition.ConverterName + ".");
}
LayerObjectValue? converted;
try
{
converted = converter(
new LayerConversionContext(SourceSchema, TargetSchema,
(value, target, fields, _) =>
TransformCompatible(value, target, fields),
TransformObject), source);
}
catch (Exception exception)
{
throw new LayerUncoveredException("Semantic converter " +
disposition.ConverterName +
" failed: " + exception.Message);
}
if (converted == null)
{
throw new LayerUncoveredException("Semantic converter " +
disposition.ConverterName +
" did not produce a value.");
}
if (!TargetSchema.TryGetConstructor(converted.Constructor.Name, out _))
{
throw new LayerUncoveredException("Semantic converter " +
disposition.ConverterName +
" produced an unknown target constructor.");
}
return converted;
case LayerDispositionKind.Unwrap:
LayerField unwrapped = UnwrappedField(source, disposition, out object? value);
if (unwrapped.Type.Kind != LayerWireKind.Object)
{
throw new LayerUncoveredException("Only a root result can unwrap " +
source.Constructor.Name + "." +
unwrapped.Name + ".");
}
return TransformObject((LayerObjectValue)value!);
case LayerDispositionKind.Unsupported:
case LayerDispositionKind.ProvenNotEmitted:
case LayerDispositionKind.NoTargetConstructor:
case LayerDispositionKind.Omit:
case LayerDispositionKind.Stream:
throw new LayerUncoveredException("Constructor disposition " +
disposition.Kind + " cannot transform " +
source.Constructor.Name + ".");
default:
throw new LayerUncoveredException("Unknown constructor disposition.");
}
}
if (!TargetSchema.TryGetConstructor(source.Constructor.Name,
out LayerConstructor target) ||
!source.Constructor.HasSameShape(target))
{
throw new LayerUncoveredException("Constructor is not covered: " +
source.Constructor.Name + ".");
}
return TransformMechanical(source, source.Constructor.Name);
}
private LayerObjectValue TransformMechanical(LayerObjectValue source,
string targetConstructorName)
{
return TransformCompatible(source, targetConstructorName, null);
}
private LayerObjectValue TransformCompatible(LayerObjectValue source,
string targetConstructorName,
IReadOnlyDictionary<string, object?>? replacements)
{
LayerConstructor target = TargetSchema.GetConstructor(targetConstructorName);
var fields = new Dictionary<string, object?>(StringComparer.Ordinal);
foreach (LayerField targetField in target.Fields)
{
if (targetField.Type.Kind == LayerWireKind.Flags)
{
continue;
}
if (replacements != null &&
replacements.TryGetValue(targetField.Name, out object? replacement))
{
fields.Add(targetField.Name, replacement);
continue;
}
LayerField? sourceField = source.Constructor.FindField(targetField.Name);
if (sourceField == null)
{
if (targetField.IsConditional)
{
continue;
}
throw new LayerUncoveredException("Target field has no source field: " +
targetField.Name + ".");
}
if (!source.TryGetValue(sourceField.Name, out object? value))
{
if (targetField.IsConditional)
{
continue;
}
throw new LayerUncoveredException("Required target field is absent: " +
targetField.Name + ".");
}
if (!sourceField.Type.HasSameWireType(targetField.Type))
{
throw new LayerUncoveredException("Target field has an incompatible source field: " +
targetField.Name + ".");
}
fields.Add(targetField.Name,
TransformValue(sourceField.Type, targetField.Type, value));
}
return new LayerObjectValue(target, fields);
}
private bool IsOmitted(object? value)
{
return value is LayerObjectValue item &&
_dispositions.TryGetValue(item.Constructor.Name,
out LayerConstructorDisposition? disposition) &&
disposition.Kind == LayerDispositionKind.Omit;
}
private object? TransformValue(LayerType sourceType, LayerType targetType,
object? value)
{
if (!sourceType.HasSameWireType(targetType))
{
throw new LayerUncoveredException("Mechanical value types differ.");
}
if (value == null)
{
return null;
}
if (sourceType.Kind == LayerWireKind.Object)
{
return TransformObject((LayerObjectValue)value);
}
if (sourceType.Kind == LayerWireKind.Vector)
{
var sourceValues = (IReadOnlyList<object?>)value;
var targetValues = new List<object?>(sourceValues.Count);
foreach (object? sourceValue in sourceValues)
{
if (!IsOmitted(sourceValue))
{
targetValues.Add(TransformValue(sourceType.ElementType!,
targetType.ElementType!, sourceValue));
}
}
return targetValues.AsReadOnly();
}
return value;
}
}