alef 0.25.37

Opinionated polyglot binding generator for Rust libraries
Documentation
/// <summary>Static helpers for registering trait implementations</summary>
public static class {{ trait_pascal }}Registry {

    private static readonly ConcurrentDictionary<string, {{ trait_pascal }}Bridge> _bridges =
        new ConcurrentDictionary<string, {{ trait_pascal }}Bridge>();

    /// <summary>Register a {{ trait_pascal }} implementation and return its native handle</summary>
    public static IntPtr Register{{ trait_pascal }}(I{{ trait_pascal }} impl) {
        if (impl == null)
            throw new ArgumentNullException(nameof(impl));

        var bridge = new {{ trait_pascal }}Bridge(impl);
        var userData = bridge._bridgeId;
        var name = {% if has_super_trait %}impl.Name{% else %}"bridge_" + Guid.NewGuid().ToString(){% endif %};

        lock ({{ trait_pascal }}Bridge._registryLock) {
            {{ trait_pascal }}Bridge._bridgeRegistry[userData] = bridge;
        }
        return userData;
    }

    /// <summary>Register a {{ trait_pascal }} implementation and return its native handle</summary>

{% if has_super_trait %}
    public static IntPtr Register(I{{ trait_pascal }} impl) {
        if (impl == null)
            throw new ArgumentNullException(nameof(impl));

        var name = impl.Name;

{% else %}
    public static IntPtr Register(I{{ trait_pascal }} impl, string name) {
        if (impl == null)
            throw new ArgumentNullException(nameof(impl));

{% endif %}

        var bridge = new {{ trait_pascal }}Bridge(impl);

        try {
            var userData = bridge._bridgeId;
            var vtablePtr = bridge._vtable;

            // Register bridge in the static registry using its unique ID.
            // This keeps the bridge alive while Rust holds the ID (userData).
            lock ({{ trait_pascal }}Bridge._registryLock) {
                {{ trait_pascal }}Bridge._bridgeRegistry[userData] = bridge;
            }

            var result = NativeMethods.Register{{ trait_pascal }}(name, vtablePtr, userData, out var outError);
            if (result != 0) {
                lock ({{ trait_pascal }}Bridge._registryLock) {
                    {{ trait_pascal }}Bridge._bridgeRegistry.Remove(userData);
                }
                bridge.Dispose();
                var errorMsg = global::System.Runtime.InteropServices.Marshal.PtrToStringUTF8(outError) ?? "Unknown error";
                global::System.Runtime.InteropServices.Marshal.FreeCoTaskMem(outError);
                throw new InvalidOperationException($"Failed to register {name}: {errorMsg}");
            }

            return userData;
        } catch {
            lock ({{ trait_pascal }}Bridge._registryLock) {
                {{ trait_pascal }}Bridge._bridgeRegistry.Remove(bridge._bridgeId);
            }
            bridge.Dispose();
            throw;
        }
    }

{% if has_unregister %}
    /// <summary>Unregister a {{ trait_pascal }} implementation</summary>
    public static void Unregister(string name) {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("Name cannot be empty", nameof(name));

        var result = NativeMethods.Unregister{{ trait_pascal }}(name, out var outError);
        if (result != 0) {
            var errorMsg = global::System.Runtime.InteropServices.Marshal.PtrToStringUTF8(outError) ?? "Unknown error";
            global::System.Runtime.InteropServices.Marshal.FreeCoTaskMem(outError);
            throw new InvalidOperationException($"Failed to unregister {name}: {errorMsg}");
        }

        if (_bridges.TryRemove(name, out var bridge)) {
            bridge.Dispose();
        }
    }
{% endif %}

{% if has_clear %}
    /// <summary>Clear all registered {{ trait_pascal }} implementations</summary>
    public static void Clear() {
        var result = NativeMethods.Clear{{ trait_pascal }}(out var outError);
        if (result != 0) {
            var errorMsg = global::System.Runtime.InteropServices.Marshal.PtrToStringUTF8(outError) ?? "Unknown error";
            global::System.Runtime.InteropServices.Marshal.FreeCoTaskMem(outError);
            throw new InvalidOperationException($"Failed to clear {{ trait_pascal }} registry: {errorMsg}");
        }

        _bridges.Clear();
    }
{% endif %}
}