factory
is a structured, type-safe source generation tool. It is intended to be a replacement for (and improvement over) the gyb
tool!
factory
is powered by swift-syntax
, so it evolves with the toolchain. You cannot run it with the 5.6.2 release toolchain, because the 5.6.2 toolchain is too old and does not support the API factory
needs to transform your sources safely.
The current toolchain pin is:
swift-DEVELOPMENT-SNAPSHOT-2022-12-17-a
Swift files generated by factory
are still backwards compatible, in fact one of the main use cases of factory
is back-deployment.
SPF officially supports Linux and macOS.
Please note that on macOS, SPM sets the wrong @rpath
by default, so you will need to manually add a symlink to lib_InternalSwiftSyntaxParser.dylib
inside your build artifacts directory.
factory
was designed with the following goals in mind:
-
Template files should look like normal
.swift
files, so highlighters and IDEs don’t freak out. -
Templates should be safe, and prohibit arbitrary string splicing or token substitutions.
-
Templates should work well with documentation comments.
-
Template syntax should be minimal, purely additive, and source generation tooling should accept and return vanilla
.swift
sources unchanged. -
Template users should be able to use as much or as little templating as they like, and using templating on one declaration should not increase the cognitive burden of the rest of the code in the file.
-
Templating systems should nudge users towards using the least amount of templating necessary for their use-case.
-
Template sources should be self-explanatory, and understandable by developers who have never heard of
swift-package-factory
. -
Template users should be able to stop using a templating system, and be able to assume responsibility for maintaining generated
.swift
files at any time.
In a nutshell:
extension Int
{
@matrix(__ordinal__: [i, j, k], __value__: [0, 1, 2])
@inlinable public
var __ordinal__:Int
{
__value__
}
@basis
let cases:[Never] = [a, b]
enum Cases:Int
{
@matrix(__case__: cases)
case __case__
}
@matrix(__case__: cases)
public static
var __case__:Self
{
Cases.__case__.rawValue
}
}
extension Int
{
@inlinable public
var i:Int
{
0
}
@inlinable public
var j:Int
{
1
}
@inlinable public
var k:Int
{
2
}
enum Cases:Int
{
case a
case b
}
public static
var a:Self
{
Cases.a.rawValue
}
public static
var b:Self
{
Cases.b.rawValue
}
}
Check out the Examples
directory to learn how to use SPF!
factory
extends the Swift language with three attributes:
-
@basis
Defines a sequence of tokens to iterate over when generating declarations from a template. It can be applied to a
let
binding, and it must be initialized with an array literal.The declaration it is attached to will be removed from the generated
.swift
code, along with any associated comments and doccomments. -
@matrix
Replicates the declaration it is attached to, along with any associated comments and doccomments. It takes
@basis
bindings or and/or inline array literals as arguments, with the name of the argument becoming the name of the loop variable. If more than one basis is given,@matrix
zips them, and will discard trailing basis elements if their lengths differ.@matrix
can be applied to anassociatedtype
,actor
,class
,case
,enum
,extension
,func
,import
,init
,operator
, precedence group,protocol
,struct
,subscript
,typealias
,let
, orvar
declaration. -
@retro
Downgrades a protocol with primary associated types to a protocol without any, and gates the two variants by
#if swift(>=5.7)
. It can be applied to aprotocol
with at least one primaryassociatedtype
.@retro
copies any comments and doccomments attached to the originalprotocol
, and includes them in the generated#if
blocks.