1
0
Fork 0

Update MonorepoConfig.from to use [packages.Name] section format, update tests

This commit is contained in:
Brendan Szymanski 2026-07-01 20:55:16 -04:00
parent 880c1574d5
commit 73d4b442b5
3 changed files with 66 additions and 21 deletions

View file

@ -474,19 +474,29 @@ public struct IncludeEntry {
} }
extension MonorepoConfig { extension MonorepoConfig {
/// Parses a monorepo TOML config file. /// Parses a monorepo TOML config file using `[packages.Name]` section headers
/// (the TOML reader supports `[section]` headers but not `[[array-of-tables]]`).
///
/// Example config:
/// ```toml
/// output_dir = "."
///
/// [packages.GLib]
/// gir = "/usr/share/gir-1.0/GLib-2.0.gir"
///
/// [packages.GObject]
/// gir = "/usr/share/gir-1.0/GObject-2.0.gir"
/// concurrency = "mainActor"
/// ```
public static func from(toml: [String: Any], defaultOutputDir: String) throws -> MonorepoConfig { public static func from(toml: [String: Any], defaultOutputDir: String) throws -> MonorepoConfig {
let outputDir = toml["output_dir"] as? String ?? defaultOutputDir let outputDir = toml["output_dir"] as? String ?? defaultOutputDir
guard let pkgArray = toml["packages"] as? [[String: Any]] else { guard let packagesDict = toml["packages"] as? [String: [String: Any]] else {
throw TOMLReaderError.parseError(line: 0, message: "monorepo config missing [[packages]] array") throw TOMLReaderError.parseError(line: 0, message: "monorepo config missing [packages.*] sections")
} }
var packages: [PackageEntry] = [] var packages: [PackageEntry] = []
for (index, pkgDict) in pkgArray.enumerated() { for (name, pkgDict) in packagesDict.sorted(by: { $0.key < $1.key }) {
guard let name = pkgDict["name"] as? String else {
throw TOMLReaderError.parseError(line: 0, message: "packages[\(index)] missing 'name'")
}
guard let girPath = pkgDict["gir"] as? String else { guard let girPath = pkgDict["gir"] as? String else {
throw TOMLReaderError.parseError(line: 0, message: "packages[\(index)].\(name) missing 'gir'") throw TOMLReaderError.parseError(line: 0, message: "packages.\(name) missing 'gir'")
} }
let concurrency = (pkgDict["concurrency"] as? String).flatMap(ConcurrencyModel.init(rawValue:)) let concurrency = (pkgDict["concurrency"] as? String).flatMap(ConcurrencyModel.init(rawValue:))
let objects = try parseMonorepoObjectConfigs(from: pkgDict) let objects = try parseMonorepoObjectConfigs(from: pkgDict)

View file

@ -8,7 +8,16 @@ struct MonorepoConfigTests {
@Test func parsesValidMonorepoConfig() throws { @Test func parsesValidMonorepoConfig() throws {
let toml = """ let toml = """
output_dir = "." output_dir = "."
packages = [{ name = "Gtk", gir = "/usr/share/gir-1.0/Gtk-4.0.gir" },{ name = "Gdk", gir = "/usr/share/gir-1.0/Gdk-4.0.gir", concurrency = "mainActor" },{ name = "GObject", gir = "/usr/share/gir-1.0/GObject-2.0.gir" }]
[packages.Gtk]
gir = "/usr/share/gir-1.0/Gtk-4.0.gir"
[packages.Gdk]
gir = "/usr/share/gir-1.0/Gdk-4.0.gir"
concurrency = "mainActor"
[packages.GObject]
gir = "/usr/share/gir-1.0/GObject-2.0.gir"
""" """
let dict = try TOMLReader.parse(toml) let dict = try TOMLReader.parse(toml)
@ -16,23 +25,22 @@ struct MonorepoConfigTests {
#expect(config.outputDir == ".") #expect(config.outputDir == ".")
#expect(config.packages.count == 3) #expect(config.packages.count == 3)
#expect(config.packages[0].name == "Gtk") #expect(config.packages[0].name == "GObject")
#expect(config.packages[0].girPath == "/usr/share/gir-1.0/Gtk-4.0.gir") #expect(config.packages[0].girPath == "/usr/share/gir-1.0/GObject-2.0.gir")
#expect(config.packages[0].concurrency == nil) #expect(config.packages[0].concurrency == nil)
#expect(config.packages[1].name == "Gdk") #expect(config.packages[1].name == "Gdk")
#expect(config.packages[1].concurrency == .mainActor) #expect(config.packages[1].concurrency == .mainActor)
#expect(config.packages[2].name == "GObject") #expect(config.packages[2].name == "Gtk")
#expect(config.packages[2].concurrency == nil) #expect(config.packages[2].concurrency == nil)
} }
@Test func parsesPackagesWithObjectOverrides() throws { @Test func parsesPackagesWithObjectOverrides() throws {
let dict: [String: Any] = [ let dict: [String: Any] = [
"output_dir": "Sources", "output_dir": "Sources",
"packages": [ "packages": [
[ "Gtk": [
"name": "Gtk", "gir": "/usr/share/gir-1.0/Gtk-4.0.gir",
"gir": "/usr/share/gir-1.0/Gtk-4.0.gir", "object": [
"object": [
"Gtk.Widget": ["status": "generate", "concurrency": "mainActor"], "Gtk.Widget": ["status": "generate", "concurrency": "mainActor"],
"Gtk.Legacy": ["status": "ignore"], "Gtk.Legacy": ["status": "ignore"],
], ],
@ -60,15 +68,17 @@ struct MonorepoConfigTests {
} }
} }
@Test func rejectsMissingName() { @Test func rejectsMissingGir() throws {
let dict: [String: Any] = [ let dict: [String: Any] = [
"packages": [ "packages": [
["gir": "/usr/share/gir-1.0/Gtk-4.0.gir"], "Broken": [
"concurrency": "mainActor",
],
], ],
] ]
do { do {
_ = try MonorepoConfig.from(toml: dict, defaultOutputDir: ".") _ = try MonorepoConfig.from(toml: dict, defaultOutputDir: ".")
Issue.record("Expected error for missing name") Issue.record("Expected error for missing gir")
} catch { } catch {
// Expected // Expected
} }
@ -77,7 +87,7 @@ struct MonorepoConfigTests {
@Test func usesDefaultOutputDirWhenNotSpecified() throws { @Test func usesDefaultOutputDirWhenNotSpecified() throws {
let dict: [String: Any] = [ let dict: [String: Any] = [
"packages": [ "packages": [
["name": "Gtk", "gir": "/usr/share/gir-1.0/Gtk-4.0.gir"], "Gtk": ["gir": "/usr/share/gir-1.0/Gtk-4.0.gir"],
], ],
] ]
@ -85,4 +95,19 @@ struct MonorepoConfigTests {
#expect(config.outputDir == "/custom/output") #expect(config.outputDir == "/custom/output")
} }
@Test func preservesSortedOrder() throws {
let dict: [String: Any] = [
"packages": [
"Zeta": ["gir": "/z"],
"Alpha": ["gir": "/a"],
],
]
let config = try MonorepoConfig.from(toml: dict, defaultOutputDir: ".")
#expect(config.packages.count == 2)
#expect(config.packages[0].name == "Alpha")
#expect(config.packages[1].name == "Zeta")
}
} }

10
monorepo-config.toml Normal file
View file

@ -0,0 +1,10 @@
output_dir = "/tmp/monorepo-gen"
[packages.GLib]
gir = "/usr/share/gir-1.0/GLib-2.0.gir"
[packages.GObject]
gir = "/usr/share/gir-1.0/GObject-2.0.gir"
[packages.Gio]
gir = "/usr/share/gir-1.0/Gio-2.0.gir"