using System.Text.Json; using System.Text.Json.Serialization; using Ghost.NativeWrapperGen.Transform; namespace Ghost.NativeWrapperGen.Config; public sealed class WrapperConfig { public required string LibraryName { get; init; } public required string NativeNamespace { get; init; } public required string OutputNamespace { get; init; } public required string NativeTypePrefix { get; init; } public List SkipTypes { get; init; } = []; public List SkipFunctions { get; init; } = []; public List Remaps { get; init; } = []; public List Actions { get; init; } = []; } /// /// Describes how to remap a native type to a C# type at a call site. /// public sealed class RemapConfig { /// Native C# type to match (e.g. "sbyte*", "ufbx_string"). public required string Src { get; init; } /// C# type to expose in the generated method signature (e.g. "ReadOnlySpan<byte>"). public required string Dst { get; init; } /// Which scopes this remap applies to: "parameter" and/or "return". public List Scope { get; init; } = []; /// Optional regex patterns applied to parameter names. If specified, only matching params are remapped. public List? Filter { get; init; } /// If set, a sibling parameter with this suffix is consumed and replaced by the given expression. public DerivesFromConfig? DerivesFrom { get; init; } /// How to convert between src and dst. public AdapterConfig? Adapter { get; init; } } /// /// Describes a parameter that is derived from another (e.g. name_len derived from name.Length). /// public sealed class DerivesFromConfig { /// The prefix of the sibling parameter name to consume (e.g. "name_"). public string ParamPrefix { get; init; } = string.Empty; /// The suffix of the sibling parameter name to consume (e.g. "_len"). public string ParamSuffix { get; init; } = string.Empty; /// Expression to pass in place of the consumed parameter. $arg is replaced with the source param name. public required string Expr { get; init; } } /// /// Adapter: how to convert between src (native) and dst (C#) types. /// public sealed class AdapterConfig { /// dst → src conversion: wraps the call site and substitutes the argument. public ConvertBackConfig? ConvertBack { get; init; } /// src → dst conversion expression (for return values). $result is replaced with the src expression. public string? ConvertTo { get; init; } } /// /// Specifies how to wrap the generated call and what expression to pass for the remapped parameter. /// Magic variables: $arg = C# parameter name, $CALL = the full Api.xxx(...) call expression. /// public sealed class ConvertBackConfig { /// /// Template that wraps the entire call. Use $arg for the parameter name, $CALL for the call. /// Example: "fixed (byte* p$arg = $arg) { $CALL }" /// public required string WrapCall { get; init; } /// /// Expression passed as the native argument. Use $arg for the parameter name. /// Example: "(sbyte*)p$arg" /// public required string PassAs { get; init; } } /// /// Routing rule: determines where a function gets emitted and as what kind of method. /// public sealed class ActionConfig { /// "EXTERN_API" = all DllImport methods on the Api class. public required string Filter { get; init; } /// /// Conditions that must all be true: /// "SELF_PTR" — first param type is T* where T is a known binding struct /// "FIRST_PARAM_OTHER_TYPE" — first param is NOT a known struct pointer /// "RETURN_BINDING_TYPE" — return type is T* where T is a known binding struct /// "VOID_RETURN" — return type is void /// "NAME_CONDITION(regex)" — native function name matches the regex ($TSelf/$TBare substituted) /// public List Conditions { get; init; } = []; /// "FIRST_PARAM_TYPE" or "RETURN_TYPE" — which type the method is placed on. public required string TargetType { get; init; } /// /// One or more apply steps. In JSON can be a single object or an array. /// Supported types: "INSTANCE_METHOD", "STATIC_METHOD", "INHERITANCE". /// [JsonConverter(typeof(ActionApplyListConverter))] public required List Apply { get; init; } public string? Comment { get; init; } } /// /// Describes a single apply step within an action. /// public sealed class ActionApplyConfig { /// "INSTANCE_METHOD", "STATIC_METHOD", or "INHERITANCE". public required string Type { get; init; } /// /// Optional per-apply-step options. Accessed dynamically: /// opts.removeFirstParam — bool [INSTANCE_METHOD] skip first param from public signature /// opts.passAs — string [INSTANCE_METHOD] self-pointer expression ($TSelf substituted) /// opts.name.set — string [INSTANCE/STATIC] fixed method name /// opts.name.remove — array [INSTANCE/STATIC] removal token list /// opts.baseType — array [INHERITANCE] list of base type strings /// [JsonConverter(typeof(DynamicJsonConverter))] public dynamic? Opts { get; init; } } /// /// Deserializes the "apply" JSON field as either a single object or an array of objects, /// always producing a List<ActionApplyConfig>. /// internal sealed class ActionApplyListConverter : JsonConverter> { public override List Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.StartArray) { return JsonSerializer.Deserialize>(ref reader, options) ?? []; } if (reader.TokenType == JsonTokenType.StartObject) { var single = JsonSerializer.Deserialize(ref reader, options); return single is not null ? [single] : []; } throw new JsonException($"Expected object or array for 'apply', got {reader.TokenType}."); } public override void Write(Utf8JsonWriter writer, List value, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, value, options); }