1
0
Fork 0
gobject-generator/Sources/SwiftGtkGenCore/BindingPlan.swift
Brendan Szymanski dac435ec89 Add Tier 4 Gdk/Gsk bindings and E3 review remediation
Generates Gdk and Gsk wrappers (330/417 and 140/183 callables), with runtime smoke tests linked against real libgtk-4. Also fixes a latent cross-module dropped-type reference bug, corrects the init(takingOwnership:) doc for no-free records, restores a precise filename safety check, and adds unit test coverage for four previously compile-gate-only branches.
2026-07-20 12:37:54 -04:00

779 lines
36 KiB
Swift

// BindingPlan.swift
// Types describing the outcome of binding analysis: which symbols were bound,
// which were skipped, and why. This file is the foundation of the binding-plan
// layer the explicit marshalling plans (`CallablePlan` et al.) that replace
// direct IR-to-string code generation are added here as the layer is built out.
import Foundation
/// Whether a mapped type's Swift declaration has been generated yet.
/// The planner uses this to defer callables that reference types not yet
/// emitted (e.g. functions taking `Source` before the `Source` class is
/// generated).
public enum BindingCategory: String, Equatable, Sendable {
/// Primitive, enum, bitfield, string, or alias no dependencies.
case ready
/// Object or interface needs a class/stub before callables can use it.
case needsClass
/// Boxed record needs a record wrapper.
case needsRecord
/// Callback type needs a typealias before callables can use it.
case callback
/// A type that will never be generated (foreign, unknown).
case unavailable
}
// MARK: - Value Marshalling
/// How a Swift value is marshalled **into** a C function call argument.
///
/// Describes the transformation applied to each parameter before the call.
/// The renderer never sees `GIRType` it only combines these marshalling
/// shapes with the plan's type names.
public enum MarshalIn: Equatable, Sendable {
/// Passed directly with no conversion (e.g. `Int32` `gint32`).
case direct
/// Passed through a numeric cast to the target type (e.g. `Int` `gint8`).
/// - Parameter targetType: The Swift numeric type to cast to.
case numericCast(targetType: String)
/// Convert `Bool` to `gboolean` via `? 1 : 0`.
case boolToGboolean
/// Convert `String` to a C string (`withCString` / `utf8` pointer).
case stringToC
/// Access the underlying pointer of an object or interface wrapper.
case objectPointer
/// Access the underlying pointer of an interface-typed wrapper (a
/// protocol existential `any Foo`).
case interfacePointer
/// Pass the `rawValue` of an enum (Int C int).
case enumRaw
/// Pass the `rawValue` of a bitfield (UInt32 C uint).
case bitfieldRaw
/// Pass the pointer of a boxed record wrapper.
case boxedPointer
/// Box a Swift closure for C callback trampoline dispatch.
/// - Parameters:
/// - scope: The callback lifetime from the GIR `scope` attribute.
/// - destroyTrampoline: The per-signature destroy trampoline C name,
/// or `""` when no destroy is needed (namespace-level typealiases).
case callbackBox(scope: CallbackScope?, destroyTrampoline: String)
/// Unsupported causes the whole callable to be skipped.
/// - Parameter reason: Why the parameter cannot be marshalled.
case unsupported(reason: String)
}
/// How a C return value is marshalled **out** into Swift.
///
/// Describes the transformation applied to the C function's return value
/// to produce the Swift-typed result that callers see.
public enum MarshalOut: Equatable, Sendable {
/// Direct assignment with no conversion.
case direct
/// Cast from a C numeric type to the target Swift type.
/// - Parameter fromType: The C type being cast from.
case numericCast(fromType: String)
/// Convert `gboolean` (Int32) to `Bool` via `!= 0`.
case gbooleanToBool
/// Copy a C string, optionally freeing the source.
/// - Parameter free: When `true`, the caller owns the string and must `g_free` it.
/// - Parameter constPointee: Whether the C out-param pointee is `const
/// char*` (`true`) rather than mutable `char*` (`false`). Only
/// meaningful for out-parameters (see `outParamLocalType`); irrelevant
/// for in-params (bridged via `withCString`) and return values.
case stringCopy(free: Bool, constPointee: Bool = true)
/// Wrap an object pointer, optionally sinking a floating ref.
/// - Parameter sink: When `true`, call `g_object_ref_sink` (for InitiallyUnowned constructors).
case objectWrap(sink: Bool)
/// Wrap an object pointer with `g_object_ref` (for transfer=none returns).
case objectRetain
/// Convert an integer raw value to an enum case.
/// - Parameter swiftType: The fully qualified Swift enum type name.
case enumFromRaw(swiftType: String)
/// Convert a C flags value to a Swift `OptionSet` bitfield.
/// - Parameter swiftType: The fully qualified Swift bitfield type name.
case bitfieldFromRaw(swiftType: String)
/// Wrap a boxed record pointer, optionally copying.
/// - Parameter copy: When `true`, call the copy function to take a reference.
/// - Parameter copyFunction: The C copy/ref function from the GIR (e.g. `g_value_copy`).
case boxedWrap(copy: Bool, copyFunction: String?)
/// Wrap an object pointer as the concrete `<Name>Ref` interface wrapper,
/// returned as the protocol existential.
/// - Parameter adopt: When `true`, take ownership (`init(takingOwnership:)`,
/// transfer=full); when `false`, retain a borrowed reference
/// (`init(retaining:)`, transfer=none).
case interfaceWrap(adopt: Bool)
/// Unsupported causes the whole callable to be skipped.
/// - Parameter reason: Why the return value cannot be marshalled.
case unsupported(reason: String)
}
/// How a type is accessed through the GValue machinery.
///
/// Properties without a GIR `getter=` / `setter=` annotation fall back to
/// `g_object_get` / `g_object_set`, which dispatches through the GType system.
/// These ops supply the correct `g_value_get_*` / `g_value_set_*` calls for
/// the type. Any numeric bridging the getter needs (e.g. `g_value_get_enum`
/// returns `gint` while a Swift enum uses an `Int` raw value) is applied by
/// the renderer via `numericCast`, keyed off `getterSuffix`.
public struct GValueOps: Equatable, Sendable {
/// The GType macro name used with `g_object_get`/`_set`.
/// Examples: `"G_TYPE_INT"`, `"G_TYPE_STRING"`, `"G_TYPE_OBJECT"`.
public let typeMacro: String
/// The `g_value_get_*` function suffix, e.g. `"int"`, `"enum"`, `"boxed"`.
public let getterSuffix: String
/// The `g_value_set_*` function suffix, e.g. `"int"`, `"enum"`, `"boxed"`.
public let setterSuffix: String
public init(typeMacro: String, getterSuffix: String, setterSuffix: String) {
self.typeMacro = typeMacro; self.getterSuffix = getterSuffix
self.setterSuffix = setterSuffix
}
}
/// The reason a GIR symbol was excluded from binding generation.
///
/// Every symbol the generator cannot (or should not) bind is skipped as a
/// whole, with one of these reasons recorded in the module's ``SkipReport``.
/// The compile gate diffs reports against a committed baseline: a new skip in
/// a category the generator claims to support is a regression.
public enum SkipReason: String, Codable, CaseIterable, Sendable {
/// The symbol references a namespace with no loaded GIR (e.g. `cairo`).
case foreignNamespace
/// A parameter, return, or field type could not be resolved.
case unknownType
/// The callable is variadic (`...` / `va_list`), which cannot be bridged.
case varargs
/// The callable has an unsupported `direction="out"` parameter.
case outParameter
/// The callable has an unsupported `direction="inout"` parameter.
case inoutParameter
/// A C array parameter or return has no length annotation.
case arrayWithoutLength
/// The type involves a container (GList, GSList, GHashTable, ) that is
/// not yet bridged.
case containerType
/// A callback parameter has no user-data slot, so a Swift closure cannot
/// be attached.
case callbackWithoutUserData
/// A callback parameter has a `scope` the generator does not support.
case unsupportedCallbackScope
/// The record has no GType registration and no configured lifetime
/// functions, so it cannot be safely wrapped.
case plainRecord
/// The record is a class/interface structure (`glib:is-gtype-struct-for`)
/// and is never bound.
case gtypeStruct
/// The symbol is marked `introspectable="0"` in the GIR.
case notIntrospectable
/// The symbol is deprecated and configured for removal.
case deprecatedRemoved
/// The symbol is shadowed by another (`shadowed-by`).
case shadowedSymbol
/// The symbol was explicitly ignored via configuration.
case configIgnored
/// A property type has no supported GValue accessor category.
case unsupportedGValueCategory
/// The symbol's Swift name collides with another symbol in the same
/// module after case conversion (e.g. `CSET_A_2_Z` and `CSET_a_2_z`
/// both map to `csetA2Z`); the first occurrence wins.
case nameCollision
/// A method in an interface protocol requirement has no consistently-
/// witnessed signature across all implementing classes. The requirement
/// is dropped from the protocol to keep it compilable.
case interfaceMethodSignatureDrift
/// A boxed record returned `transfer-ownership="none"` has no copy/ref
/// function but a free function, so adopting the borrowed pointer as
/// owned would double-free. The callable is skipped.
case boxedBorrowedWithoutCopy
/// A constructor has out-params, which cannot be expressed as a Swift
/// `init` (an init cannot also return out-value tuples). The constructor
/// is skipped.
case constructorOutParams
/// A signal parameter or return type could not be mapped.
case signalUnmappableParam
/// A type declaration duplicates one already declared in a dependency
/// module (e.g. GObject re-declaring GLib's `IOCondition`); the
/// downstream duplicate is skipped so unqualified cross-module
/// references stay unambiguous.
case duplicateOfDependency
/// A class member (property or method) is already declared, with an
/// identical Swift name, by an ancestor class planned in this module;
/// the subclass copy is redundant (GObject inheritance provides it).
case inheritedMember
}
/// A single skipped symbol: what was skipped, and why.
///
/// Entries are written to `skip-reports/<Module>.json` by the CLI and diffed
/// by `scripts/compile-gate.sh` against `docs/skip-baseline/`.
public struct SkipEntry: Codable, Equatable, Sendable {
/// Fully qualified GIR symbol name, e.g. `"Gtk.Widget.measure"`.
public let symbol: String
/// The C identifier of the symbol, when known (e.g. `"gtk_widget_measure"`).
public let cIdentifier: String?
/// The category of the skip.
public let reason: SkipReason
/// Human-readable specifics, e.g. `"parameter 'baseline' has direction=out"`.
public let detail: String
/// Creates a skip entry.
///
/// - Parameters:
/// - symbol: Fully qualified GIR symbol name.
/// - cIdentifier: The C identifier, if known.
/// - reason: The skip category.
/// - detail: Human-readable specifics for the report.
public init(symbol: String, cIdentifier: String? = nil, reason: SkipReason, detail: String) {
self.symbol = symbol
self.cIdentifier = cIdentifier
self.reason = reason
self.detail = detail
}
}
/// Binding coverage statistics for one generated module.
///
/// Coverage is the fraction of introspectable callables that received a
/// complete binding plan. The compile gate requires coverage to be
public struct CoverageStats: Codable, Equatable, Sendable {
/// Number of callables successfully planned and emitted.
public var boundCallables: Int
/// Total introspectable callables considered (bound + skipped).
public var totalCallables: Int
/// Number of types successfully planned and emitted.
public var boundTypes: Int
/// Total introspectable types considered (bound + skipped).
public var totalTypes: Int
/// Namespace-level callbacks successfully planned and emitted (D6).
public var boundCallbacks: Int
/// Total namespace-level callbacks considered (bound + skipped).
public var totalCallbacks: Int
/// GObject signals successfully planned and emitted (Phase D).
public var boundSignals: Int
/// Total signals considered (bound + skipped).
public var totalSignals: Int
public init(boundCallables: Int = 0, totalCallables: Int = 0,
boundTypes: Int = 0, totalTypes: Int = 0,
boundCallbacks: Int = 0, totalCallbacks: Int = 0,
boundSignals: Int = 0, totalSignals: Int = 0) {
self.boundCallables = boundCallables; self.totalCallables = totalCallables
self.boundTypes = boundTypes; self.totalTypes = totalTypes
self.boundCallbacks = boundCallbacks; self.totalCallbacks = totalCallbacks
self.boundSignals = boundSignals; self.totalSignals = totalSignals
}
}
/// The complete skip report for one generated module.
///
/// Serialized as stable, sorted, pretty-printed JSON so reports are
/// line-diffable by the compile gate and reviewable in version control.
public struct SkipReport: Codable, Equatable, Sendable {
/// The Swift module this report describes (e.g. `"GLib"`).
public let module: String
/// All skipped symbols, sorted by symbol name.
public let entries: [SkipEntry]
/// Coverage statistics for the module.
public let stats: CoverageStats
/// Creates a skip report, sorting entries by symbol name for stable output.
///
/// - Parameters:
/// - module: The Swift module name.
/// - entries: Skipped symbols in any order; stored sorted.
/// - stats: Coverage statistics for the module.
public init(module: String, entries: [SkipEntry], stats: CoverageStats) {
self.module = module
self.entries = entries.sorted { $0.symbol < $1.symbol }
self.stats = stats
}
/// Encodes the report as stable pretty-printed JSON with sorted keys.
///
/// - Returns: UTF-8 JSON data suitable for writing to disk and diffing.
/// - Throws: An `EncodingError` if serialization fails.
public func jsonData() throws -> Data {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
return try encoder.encode(self)
}
}
// MARK: - Type Plans
/// The binding plan for a single GIR type (enum, bitfield, constant, alias,
/// class, interface, record, or callback). The renderer consumes these and
/// never sees `GIRType`, `IRModel`, or the registry directly.
public enum TypePlan: Sendable {
/// An enumeration with deduplicated raw values.
case enumeration(EnumPlan)
/// An OptionSet bitfield with verbatim raw values.
case bitfield(BitfieldPlan)
/// A global constant.
case constant(ConstantPlan)
/// A type alias.
case alias(AliasPlan)
/// A GObject class wrapper.
case `class`(ClassPlan)
/// A GObject interface wrapper.
case interface(InterfacePlan)
/// A boxed record wrapper.
case record(RecordPlan)
/// A callable (function, method, or constructor).
case callable(CallablePlan)
/// A namespace-level callback typealias.
case callback(CallbackTypePlan)
}
/// The plan for a GIR enumeration: cases and alias variables (deduplicated
/// raw values).
public struct EnumPlan: Equatable, Sendable {
/// The Swift enum name (unqualified), e.g. `"ConnectFlags"`.
public let name: String
/// The C type name, e.g. `"GConnectFlags"`.
public let cType: String
/// The primary cases (first occurrence of each raw value).
public let cases: [EnumCase]
/// Duplicate raw values emitted as `public static var` aliases.
public let aliases: [EnumAlias]
/// Whether this enum has a GType registration (for GValue access).
public let hasGType: Bool
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(name: String, cType: String, cases: [EnumCase], aliases: [EnumAlias],
hasGType: Bool = false, doc: String? = nil) {
self.name = name; self.cType = cType; self.cases = cases
self.aliases = aliases; self.hasGType = hasGType; self.doc = doc
}
}
/// A single case of an enumeration: name, raw value, and C identifier.
public struct EnumCase: Equatable, Sendable {
/// The Swift case name (keyword-escaped), e.g. `"after"`.
public let name: String
/// The numeric raw value as a string, e.g. `"1"`.
public let rawValue: String
/// The full C identifier, e.g. `"G_CONNECT_AFTER"`.
public let cIdentifier: String
public init(name: String, rawValue: String, cIdentifier: String) {
self.name = name; self.rawValue = rawValue; self.cIdentifier = cIdentifier
}
}
/// A duplicate raw-value member emitted as a `public static var` alias.
public struct EnumAlias: Equatable, Sendable {
/// The alias variable name, e.g. `"baselineFill"`.
public let name: String
/// The primary case this alias points to, e.g. `"baseline"`.
public let targetCaseName: String
public init(name: String, targetCaseName: String) {
self.name = name; self.targetCaseName = targetCaseName
}
}
/// The plan for a GIR bitfield (flags type) emitted as an `OptionSet`.
public struct BitfieldPlan: Equatable, Sendable {
/// The Swift struct name (unqualified), e.g. `"ConnectFlags"`.
public let name: String
/// The C type name, e.g. `"GConnectFlags"`.
public let cType: String
/// The flag members with their verbatim raw values.
public let members: [EnumCase]
/// Whether this bitfield has a GType registration.
public let hasGType: Bool
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(name: String, cType: String, members: [EnumCase],
hasGType: Bool = false, doc: String? = nil) {
self.name = name; self.cType = cType; self.members = members
self.hasGType = hasGType; self.doc = doc
}
}
/// The plan for a global constant.
public struct ConstantPlan: Equatable, Sendable {
/// The Swift name of the constant, e.g. `"majorVersion"`.
public let name: String
/// The original GIR constant name, e.g. `"MAJOR_VERSION"`. Rendered into
/// the doc comment so the C spelling stays greppable after the rename.
public let girName: String
/// The literal value as a string, e.g. `"2"`.
public let value: String
/// The Swift type of the constant, e.g. `"Int"`.
public let swiftType: String
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(name: String, girName: String, value: String, swiftType: String, doc: String? = nil) {
self.name = name; self.girName = girName; self.value = value
self.swiftType = swiftType; self.doc = doc
}
}
/// The plan for a type alias.
public struct AliasPlan: Equatable, Sendable {
/// The Swift alias name, e.g. `"MyType"`.
public let name: String
/// The target type name, module-qualified when cross-module.
public let swiftType: String
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(name: String, swiftType: String, doc: String? = nil) {
self.name = name; self.swiftType = swiftType; self.doc = doc
}
}
/// The plan for a GObject signal connection.
///
/// Each signal on a class or interface generates a typed trampoline
/// (`@convention(c)` static func) and a `connect<Name>()` method that
/// boxes the Swift closure, passes it to `g_signal_connect_data`,
/// and returns a `SignalHandle`.
public struct SignalPlan: Equatable, Sendable {
/// The owning class's Swift name, e.g. `"Object"`.
public let owningClassName: String
/// The GIR signal name, e.g. `"clicked"`, `"notify"`.
public let girName: String
/// The camelCased Swift name, e.g. `"clicked"`.
public let swiftName: String
/// Whether the signal supports detail strings (`"notify::label"`).
public let isDetailed: Bool
/// The signal handler parameters (includes implicit instance param at cArgIndex 0).
public let parameters: [ParameterPlan]
/// The return mapping, or `nil` for `void`.
public let returnMapping: Mapping?
/// The `@convention(c)` trampoline's C-level name, unique per module.
public let trampolineCName: String
/// `true` when the owning type is a `protocol` (GObject interface)
/// rather than a concrete class the trampoline's instance parameter
/// must be wrapped via the interface's `Ref` concrete wrapper, not the
/// interface protocol itself (which has no initializers).
public let ownerIsInterface: Bool
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(owningClassName: String, girName: String, swiftName: String,
isDetailed: Bool = false, parameters: [ParameterPlan] = [],
returnMapping: Mapping? = nil, trampolineCName: String,
ownerIsInterface: Bool = false, doc: String? = nil) {
self.owningClassName = owningClassName; self.girName = girName
self.swiftName = swiftName; self.isDetailed = isDetailed
self.parameters = parameters; self.returnMapping = returnMapping
self.trampolineCName = trampolineCName; self.ownerIsInterface = ownerIsInterface
self.doc = doc
}
}
/// The plan for a namespace-level callback type.
///
/// Renders as both a C-compatible `@convention(c)` typealias for C callbacks
/// and a Swift-friendly `@escaping` typealias for use in generated connect methods.
public struct CallbackTypePlan: Equatable, Sendable {
/// The Swift type name, e.g. `"GClosureNotify"`.
public let name: String
/// The Swift closure form, e.g. `"@escaping (UnsafeMutableRawPointer?) -> Void"`.
public let swiftType: String
/// The C-compatible convention(c) form for trampoline signatures.
public let cSwiftType: String
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(name: String, swiftType: String, cSwiftType: String, doc: String? = nil) {
self.name = name; self.swiftType = swiftType; self.cSwiftType = cSwiftType; self.doc = doc
}
}
/// The plan for a GObject class wrapper.
public struct ClassPlan: Equatable, Sendable {
/// The Swift class name (unqualified), e.g. `"Object"`.
public let name: String
/// The C type name, e.g. `"GObject"`.
public let cType: String
/// The module-qualified Swift parent class name, or `nil` for root.
/// E.g. `nil` for `GObject.Object`, `"GObject.InitiallyUnowned"` for `Gtk.Widget`.
public let parent: String?
/// Whether the class is `open` (subclassed by at least one other type).
public let isOpen: Bool
/// Whether the class is abstract no constructors emitted.
public let isAbstract: Bool
/// The `glib:get-type` function name, e.g. `"g_object_get_type"`.
public let getTypeFunction: String?
/// Whether this class descends from `InitiallyUnowned`, requiring
/// `g_object_ref_sink` on construction.
public let descendsFromInitiallyUnowned: Bool
/// The C function that takes a reference on this class's underlying
/// pointer (`init(retaining:)`). `"g_object_ref"` for ordinary
/// `GObject`-derived classes; overridden for classes that root their own
/// non-`GObject` fundamental type hierarchy (e.g. `GParamSpec`'s
/// `g_param_spec_ref_sink` see `TypeRegistry.refUnrefFunctions`).
public let refFunc: String
/// Planned constructors (empty for abstract classes).
public let constructors: [CallablePlan]
/// Planned instance methods.
public let methods: [CallablePlan]
/// Planned static/class functions.
public let functions: [CallablePlan]
/// Planned GObject properties.
public let properties: [PropertyPlan]
/// Planned GObject signals.
public let signals: [SignalPlan]
/// Module-qualified names of implemented interfaces (e.g. ["GObject.TypePlugin"]).
public let interfaces: [String]
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(name: String, cType: String, parent: String? = nil,
isOpen: Bool = false, isAbstract: Bool = false,
getTypeFunction: String? = nil,
descendsFromInitiallyUnowned: Bool = false,
refFunc: String = "g_object_ref",
interfaces: [String] = [],
constructors: [CallablePlan] = [], methods: [CallablePlan] = [],
functions: [CallablePlan] = [], properties: [PropertyPlan] = [],
signals: [SignalPlan] = [],
doc: String? = nil) {
self.name = name; self.cType = cType; self.parent = parent
self.isOpen = isOpen; self.isAbstract = isAbstract
self.getTypeFunction = getTypeFunction
self.descendsFromInitiallyUnowned = descendsFromInitiallyUnowned
self.refFunc = refFunc
self.interfaces = interfaces
self.constructors = constructors; self.methods = methods
self.functions = functions; self.properties = properties
self.signals = signals
self.doc = doc
}
}
/// The plan for a boxed record wrapper (opaque pointer class).
public struct RecordPlan: Equatable, Sendable {
public let name: String
public let cType: String
public let getTypeFunction: String?
public let copyFunction: String?
public let freeFunction: String?
public let doc: String?
public init(name: String, cType: String, getTypeFunction: String? = nil,
copyFunction: String? = nil, freeFunction: String? = nil,
doc: String? = nil) {
self.name = name; self.cType = cType
self.getTypeFunction = getTypeFunction
self.copyFunction = copyFunction; self.freeFunction = freeFunction
self.doc = doc
}
}
/// The plan for a GObject interface wrapper.
public struct InterfacePlan: Equatable, Sendable {
public let name: String
public let cType: String
public let prereqs: [String]
/// The Swift name of a prerequisite that is a *class* (not another
/// interface), if any e.g. `TlsServerConnection` prerequisites
/// `TlsConnection`. The protocol inheritance clause then constrains
/// `Self: TlsConnection`, so the concrete `<Name>Ref` wrapper must
/// subclass it directly (inheriting its `pointer`/init/deinit) rather
/// than declaring its own bare ref-counted storage.
public let classPrereq: String?
public let getTypeFunction: String?
/// The interface's instance methods, rendered as protocol requirements.
/// The implementing class supplies each method body (the C symbol lives on
/// the class, not the interface see `planInterface`).
public let methods: [CallablePlan]
/// Properties declared by this interface, rendered as protocol requirements.
public let properties: [PropertyPlan]
/// Signals declared by this interface.
public let signals: [SignalPlan]
/// The module-qualified Swift name, e.g. `"GObject.TypePlugin"`.
/// Used to match against `ClassPlan.interfaces` entries.
public let qualifiedName: String
/// `true` when this interface's implementers are GObject-derived (i.e.
/// `var pointer` refers to a ref-counted `GObject*`). Gates emission of
/// the concrete `<Name>Ref` wrapper, which manages that refcount.
public let isGObject: Bool
public let doc: String?
public init(name: String, cType: String, prereqs: [String] = [],
classPrereq: String? = nil,
getTypeFunction: String? = nil, methods: [CallablePlan] = [],
properties: [PropertyPlan] = [], signals: [SignalPlan] = [],
qualifiedName: String = "", isGObject: Bool = true,
doc: String? = nil) {
self.name = name; self.cType = cType; self.prereqs = prereqs
self.classPrereq = classPrereq
self.getTypeFunction = getTypeFunction; self.methods = methods
self.properties = properties; self.signals = signals
self.qualifiedName = qualifiedName
self.isGObject = isGObject
self.doc = doc
}
}
/// How a single property accessor (the getter or the setter) is implemented.
///
/// The planner prefers ``delegate`` whenever GIR states a `getter=`/`setter=`
/// method that was itself successfully planned, because that method already
/// encodes the correct nullability and ownership. It falls back to ``gvalue``
/// the uniform GObject property machinery for pure GObject properties that
/// have no dedicated C accessor.
public enum PropertyAccessorPlan: Equatable, Sendable {
/// Read or write the value through the GObject GValue machinery:
/// `g_value_init(&v, typeMacro)` + `g_object_get_property` / `_set_property`
/// + `g_value_get_<suffix>` / `g_value_set_<suffix>`.
case gvalue(typeMacro: String, valueSuffix: String)
/// Forward to an already-generated accessor method on the same type, named
/// by the GIR `getter=` / `setter=` attribute (e.g. `getLabel`/`setLabel`).
/// `argumentLabel` is the setter method's parameter label, or `nil` for a
/// getter (which takes no arguments).
case delegate(method: String, argumentLabel: String?)
}
/// The plan for one GObject property: a named, typed Swift computed property.
///
/// Each accessor is planned independently as a ``PropertyAccessor`` either a
/// delegation to a generated method or the GValue machinery. A property whose
/// type has no `GValueOps` AND no delegable accessor is skipped with
/// ``SkipReason/unsupportedGValueCategory``.
public struct PropertyPlan: Equatable, Sendable {
/// The Swift property name (lowerCamelCase), e.g. `"sourceProperty"`.
public let swiftName: String
/// The original GIR property name, e.g. `"source-property"`. Used for the
/// `g_object_get_property` / `g_object_set_property` name argument.
public let girName: String
/// The Swift type of the property, e.g. `"String"`, `"Object?"`, or
/// `"BindingFlags"`. When an accessor is delegated, this is the delegated
/// method's exact type (so nullability is inherited).
public let swiftType: String
/// The read accessor. Always present even a write-only GObject property is
/// back-read through GValue, since a Swift computed property needs a getter.
public let getter: PropertyAccessorPlan
/// The write accessor, or `nil` for a read-only property. Construct-only
/// properties are read-only (a runtime setter would silently no-op).
public let setter: PropertyAccessorPlan?
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(swiftName: String, girName: String, swiftType: String,
getter: PropertyAccessorPlan, setter: PropertyAccessorPlan?, doc: String? = nil) {
self.swiftName = swiftName; self.girName = girName
self.swiftType = swiftType
self.getter = getter; self.setter = setter
self.doc = doc
}
}
/// The complete plan for one Swift module: every type planned, every symbol
/// skipped, and the coverage statistics computed from them.
public struct ModulePlan: Sendable {
/// The Swift module name, e.g. `"GLib"`.
public let module: String
/// Direct dependency modules this module's files must `import`, e.g.
/// `["GLib", "GModule", "GObject"]` for `Gio`. Sorted, deduplicated.
public let dependencyModules: [String]
/// All successfully planned types.
public let types: [TypePlan]
/// Every skipped symbol with its reason.
public let skips: [SkipEntry]
/// Coverage statistics derived from planned and skipped counts.
public let coverage: CoverageStats
public init(module: String, dependencyModules: [String] = [], types: [TypePlan], skips: [SkipEntry],
coverage: CoverageStats) {
self.module = module; self.dependencyModules = dependencyModules; self.types = types; self.skips = skips
self.coverage = coverage
}
/// The skip report serialised from this module's skips and coverage.
public var skipReport: SkipReport {
SkipReport(module: module, entries: skips, stats: coverage)
}
}
// MARK: - Callable Plans
/// The complete binding plan for one callable (function, method, or constructor).
///
/// Every parameter has been fully mapped by the TypeMapper; the renderer only
/// combines the `Mapping` values into a Swift function signature and a C call.
public struct CallablePlan: Equatable, Sendable {
/// The Swift name of the function/method/constructor (keyword-escaped).
public let name: String
/// The C function identifier, e.g. `"gtk_widget_set_visible"`.
public let cIdentifier: String
/// The parameters in Swift declaration order.
public let parameters: [ParameterPlan]
/// The return mapping, or `nil` for a void function.
public let returnMapping: Mapping?
/// `true` when this callable is a class/static function (not an instance
/// method). Constructor callables are implicitly static.
public let isStatic: Bool
/// `true` when this callable is a constructor.
public let isConstructor: Bool
/// The ownership init variant for constructor callables: `nil` for
/// non-constructors; `.takingOwnership`, `.retaining`, or
/// `.sinkingRef` for constructors.
public let ownershipInit: OwnershipInit?
/// `true` when this callable takes a trailing `GError**` and can throw.
public let throwsError: Bool
/// Documentation from the GIR `<doc>` element.
public let doc: String?
public init(name: String, cIdentifier: String, parameters: [ParameterPlan],
returnMapping: Mapping? = nil, isStatic: Bool = false,
isConstructor: Bool = false, ownershipInit: OwnershipInit? = nil,
throwsError: Bool = false, doc: String? = nil) {
self.name = name; self.cIdentifier = cIdentifier; self.parameters = parameters
self.returnMapping = returnMapping; self.isStatic = isStatic
self.isConstructor = isConstructor; self.ownershipInit = ownershipInit
self.throwsError = throwsError; self.doc = doc
}
}
/// How a constructor takes ownership of the new GObject instance.
public enum OwnershipInit: String, Equatable, Sendable {
/// `transfer-ownership="full"`: store the pointer, no additional ref.
case takingOwnership
/// `transfer-ownership="none"`, non-floating: call `g_object_ref`.
case retaining
/// The class descends from `InitiallyUnowned`: call `g_object_ref_sink`.
case sinkingRef
}
/// One parameter of a `CallablePlan`: its Swift name, C argument position,
/// and the complete type mapping.
public struct ParameterPlan: Equatable, Sendable {
/// The Swift parameter name (keyword-escaped).
public let swiftName: String
/// The zero-based index in the C function's argument list (all parameters,
/// including instance parameter).
public let cArgIndex: Int
/// The complete type mapping for this parameter.
public let mapping: Mapping
/// `true` when this parameter is the implicit instance (`self`).
public let isInstanceParameter: Bool
/// `true` if this parameter has `direction="out"` and will be returned from
/// the Swift function rather than passed as an argument.
public let isOutParameter: Bool
/// For callback-box params: the C arg index of the separate user-data
/// parameter this callback feeds. `nil` when this param doubles as
/// its own user-data (`closureIndex == cArgIndex`).
public let closureIndex: Int?
/// For callback-box params: the C arg index of the separate DestroyNotify
/// parameter. `nil` when none.
public let destroyIndex: Int?
public init(swiftName: String, cArgIndex: Int, mapping: Mapping,
isInstanceParameter: Bool = false, isOutParameter: Bool = false,
closureIndex: Int? = nil, destroyIndex: Int? = nil) {
self.swiftName = swiftName; self.cArgIndex = cArgIndex
self.mapping = mapping; self.isInstanceParameter = isInstanceParameter
self.isOutParameter = isOutParameter
self.closureIndex = closureIndex; self.destroyIndex = destroyIndex
}
}