Add qualifiedTypeToSwift and generateMonorepo for cross-namespace type references
This commit is contained in:
parent
cf9f762155
commit
3dde30dc70
1 changed files with 135 additions and 0 deletions
|
|
@ -196,6 +196,115 @@ public struct CodeGenerator {
|
|||
return files
|
||||
}
|
||||
|
||||
/// Generates Swift source for all modules in a monorepo.
|
||||
///
|
||||
/// Iterates over all packages in the analysis, producing per-type Swift
|
||||
/// files for each module. Each file includes the appropriate `import`
|
||||
/// statements for the module's direct dependencies.
|
||||
///
|
||||
/// - Parameter analysis: The resolved multi-package analysis.
|
||||
/// - Returns: Module name → (filename → source code) nested dictionary.
|
||||
/// - Throws: `CodeGenError` if generation fails.
|
||||
public func generateMonorepo(analysis: MultiPackageAnalysis) throws -> [String: [String: String]] {
|
||||
var allOutputs: [String: [String: String]] = [:]
|
||||
|
||||
for (moduleName, repo) in analysis.repositories {
|
||||
guard let cfg = analysis.packageConfigs[moduleName] else { continue }
|
||||
guard let ns = repo.namespaces.first(where: { $0.name == moduleName })
|
||||
?? repo.namespaces.first else { continue }
|
||||
|
||||
let directImports = analysis.directDependencies[moduleName] ?? []
|
||||
let nsToModule = buildNamespaceToModuleMap(from: analysis)
|
||||
_ = nsToModule
|
||||
|
||||
let rootModuleName = moduleName
|
||||
|
||||
let analyzer = Analyzer(config: cfg)
|
||||
let singleAnalysis = analyzer.analyze(repository: repo)
|
||||
|
||||
let classNames = Set(ns.classes.map(\.name))
|
||||
let shouldGenerateAll = singleAnalysis.generatedTypes.isEmpty
|
||||
|
||||
var sourceFiles: [String: String] = [:]
|
||||
|
||||
let header = fileHeader(moduleName: rootModuleName, directImports: directImports)
|
||||
|
||||
for cls in ns.classes {
|
||||
let fullName = "\(ns.name).\(cls.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(cls.name).swift"] = header + "\n" + generateClass(cls, namespace: ns.name, analysis: singleAnalysis, classTypeNames: classNames)
|
||||
}
|
||||
for enm in ns.enumerations {
|
||||
let fullName = "\(ns.name).\(enm.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(enm.name).swift"] = header + "\n" + generateEnum(enm)
|
||||
}
|
||||
for bf in ns.bitfields {
|
||||
let fullName = "\(ns.name).\(bf.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(bf.name).swift"] = header + "\n" + generateBitfield(bf)
|
||||
}
|
||||
for iface in ns.interfaces {
|
||||
let fullName = "\(ns.name).\(iface.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(iface.name).swift"] = header + "\n" + generateInterface(iface)
|
||||
}
|
||||
for cb in ns.callbacks {
|
||||
let fullName = "\(ns.name).\(cb.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(cb.name).swift"] = header + "\n" + generateCallback(cb)
|
||||
}
|
||||
for fn in ns.functions {
|
||||
let fullName = "\(ns.name).\(fn.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(Self.pascalCaseName(fn.name)).swift"] = header + "\n" + generateGlobalFunction(fn, namespace: ns.name, classTypeNames: classNames)
|
||||
}
|
||||
for rec in ns.records {
|
||||
let fullName = "\(ns.name).\(rec.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(rec.name).swift"] = header + "\n" + generateRecord(rec, namespace: ns.name, analysis: singleAnalysis, classTypeNames: classNames)
|
||||
}
|
||||
for cst in ns.constants {
|
||||
let fullName = "\(ns.name).\(cst.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(cst.name).swift"] = header + "\n" + generateConstant(cst)
|
||||
}
|
||||
for alias in ns.aliases {
|
||||
let fullName = "\(ns.name).\(alias.name)"
|
||||
if !shouldGenerateAll && !singleAnalysis.generatedTypes.contains(fullName) { continue }
|
||||
sourceFiles["\(alias.name).swift"] = header + "\n" + generateAlias(alias)
|
||||
}
|
||||
|
||||
allOutputs[rootModuleName] = sourceFiles
|
||||
}
|
||||
|
||||
return allOutputs
|
||||
}
|
||||
|
||||
/// Generates the standard file header for monorepo source files.
|
||||
private func fileHeader(moduleName: String, directImports: Set<String>) -> String {
|
||||
var imports = "import C\(moduleName)\nimport Foundation\n"
|
||||
for imp in directImports.sorted() {
|
||||
imports += "import \(imp)\n"
|
||||
}
|
||||
return """
|
||||
// Generated by SwiftGtkGen. DO NOT EDIT.
|
||||
|
||||
\(imports)
|
||||
"""
|
||||
}
|
||||
|
||||
/// Builds a mapping from GIR namespace name to Swift module name.
|
||||
private func buildNamespaceToModuleMap(from analysis: MultiPackageAnalysis) -> [String: String] {
|
||||
var map: [String: String] = [:]
|
||||
for (moduleName, repo) in analysis.repositories {
|
||||
for ns in repo.namespaces {
|
||||
map[ns.name] = moduleName
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
// MARK: - Class Generation
|
||||
|
||||
/// Generates a Swift class declaration from a GIR class definition.
|
||||
|
|
@ -954,6 +1063,32 @@ public struct CodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts a GIR type to a Swift type string, qualifying cross-namespace
|
||||
/// type references with their Swift module name prefix.
|
||||
///
|
||||
/// Cross-namespace references (`.typeRef(name, namespace: "Gdk")`) produce
|
||||
/// `Gdk.Rectangle` when the current module is not `Gdk`. Same-namespace
|
||||
/// references produce the bare `name`.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - type: The GIR type to map.
|
||||
/// - currentModule: The Swift module name currently being generated.
|
||||
/// - nsToModule: Map from GIR namespace name to Swift module name.
|
||||
/// - Returns: A qualified Swift type name.
|
||||
static func qualifiedTypeToSwift(_ type: GIRType, currentModule: String, nsToModule: [String: String]) -> String {
|
||||
switch type {
|
||||
case .typeRef(let name, let namespace):
|
||||
if let ns = namespace, let module = nsToModule[ns], module != currentModule {
|
||||
return "\(module).\(name)"
|
||||
}
|
||||
return name
|
||||
case .array(let inner): return "[\(qualifiedTypeToSwift(inner, currentModule: currentModule, nsToModule: nsToModule))]"
|
||||
case .cArray(let inner): return "UnsafeBufferPointer<\(qualifiedTypeToSwift(inner, currentModule: currentModule, nsToModule: nsToModule))>"
|
||||
case .optional(let inner): return "\(qualifiedTypeToSwift(inner, currentModule: currentModule, nsToModule: nsToModule))?"
|
||||
default: return Self.typeToSwift(type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a snake_case GIR name to a camelCase Swift property name.
|
||||
///
|
||||
/// Splits on underscores and lowercases the first part while capitalizing
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue