Skip to content

feat: Migration function pipeline #698

@tyevco

Description

@tyevco

Overview

Implement the migration function registration and execution pipeline for transforming component data between versions.

Parent Issue

Requirements

MigrateFrom Attribute

  • Create [MigrateFrom(int version, string methodName)] attribute
  • Support multiple [MigrateFrom] attributes on single component
  • Validate migration method signature at compile time
  • Error if migration method not found or wrong signature

Source Generator

  • Generate ComponentMigrationRegistry class
  • Build migration delegate lookup table
  • Migrate<T>(object source, int fromVersion) method
  • Compile migration functions as delegates (no reflection)

Serialization Integration

  • Hook into IComponentSerializer.Deserialize<T>()
  • Check version, invoke migration if needed
  • Return migrated component to caller

Error Handling

  • MigrationNotFoundException for missing migration path
  • MigrationException for migration function failures
  • Include source version, target version, component type in errors

Example

[Component(Version = 2)]
[MigrateFrom(1, nameof(MigrateFromV1))]
public partial struct Health
{
    public int Current;
    public int Max;
    public int Shield;

    private static Health MigrateFromV1(HealthV1 old)
    {
        return new Health
        {
            Current = old.Current,
            Max = old.Max,
            Shield = 0
        };
    }
}

// Generated:
public static partial class ComponentMigrationRegistry
{
    private static readonly Dictionary<(Type, int, int), Delegate> migrations = new()
    {
        [(typeof(Health), 1, 2)] = (Func<HealthV1, Health>)Health.MigrateFromV1,
    };
    
    public static T Migrate<T>(object source, int fromVersion) where T : struct, IComponent
    {
        var key = (typeof(T), fromVersion, fromVersion + 1);
        if (migrations.TryGetValue(key, out var migration))
        {
            return (T)migration.DynamicInvoke(source)!;
        }
        throw new MigrationNotFoundException(...);
    }
}

Acceptance Criteria

  • Migration functions registered via attribute
  • Migrations execute during deserialization
  • Clear errors for missing migrations
  • Migration failures include diagnostic info
  • Unit tests for single-step migrations
  • Integration tests with serialization

Technical Notes

  • Migration delegates are Func<TOld, TNew>
  • Old version types must be preserved (e.g., HealthV1)
  • Consider making old types readonly struct for safety

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions