I can't comment on Mustache as I have never used it, but when it comes to source code generation and manipulation, we use swift-syntax extensively at my company in a way that seems to be quite similar to your use case. We have a set of JSON files describing various aspects of our Lambda functions, and then we auto-generate a lot of boilerplate code, including all the request/response DTOs, DTOs for SQS and EB, injection of all the soto
clients required to interact with other AWS services, extraction and sanitation of query and path parameters, retrieval of environment variables and SSM parameters and so on. The code is generated by buildTool plugins, making the whole experience quite satisfying.
Swift-syntax is very powerful for these scenarios. It is easily composable and reusable, and, assuming you'll be using SyntaxBuilders (which I strongly suggest), it's also easy to read (most of the time).
Here is a minimal snippet of swift-syntax in action for DTO generation where you can see what I mean by composable.
struct TypeSchema {
let typeName: String
let properties: [Property]
let subTypes: [TypeSchema]
struct Property {
let name: String
let type: String
}
}
func generateDecodableStruct(for schema: TypeSchema) throws -> StructDeclSyntax {
try StructDeclSyntax("public struct \(raw: schema.typeName)Request: Decodable") {
for property in schema.properties {
"public let \(raw: property.name): \(raw: property.type)"
}
for subType in schema.subTypes {
try generateDecodableStruct(for: subType)
}
}
}
Another reason why I would advocate for swift-syntax is its integration with swift-parser and swift-format. If you are not going to use buildTool plugins, having the possibility to evaluate if a file has actually changed prior to writing it to disk, and having the possibility of formatting the source code in the same step instead of invoking swift-format
and "rewrite it in-place" later on can save you a few minutes every time (obviously we are talking about a few thousands of files here).
Last but not least, with swift-syntax you can manipulate source code in existing files. I'll bring a real example here to explain better what I mean. Our whole system was based on gyb, which means that every time we wanted to change the dependencies of a target that wasn't auto generated, we had to change our Package.swift.gyb
template and run gyb.
With swift-syntax we simply parse the whole Package.swift
file, we find the declaration of
let autogeneratedTargets: [Target] = [ /*....*/]
and then we just replace that declaration, without altering any other statement of the file. This means we can safely manage the 'non-autogenerated' targets manually directly in our Package.swift
instead of using a boring gyb file with no code completion and no syntax highlight support.