Skip to content
This repository was archived by the owner on Nov 25, 2025. It is now read-only.

Commit f80870a

Browse files
committed
initial
0 parents  commit f80870a

File tree

9 files changed

+245
-0
lines changed

9 files changed

+245
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
.swiftpm
3+
.build
4+
.vscode

Package.resolved

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// swift-tools-version: 5.9
2+
import CompilerPluginSupport
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "swift-macros",
7+
platforms: [
8+
.macOS(.v12),
9+
],
10+
products: [
11+
.executable(
12+
name: "Examples",
13+
targets: ["Examples"]
14+
),
15+
.library(
16+
name: "SwiftMacros",
17+
targets: ["SwiftMacros"]
18+
),
19+
],
20+
dependencies: [
21+
.package(
22+
url: "https://github.com/apple/swift-syntax.git",
23+
branch: "main"
24+
),
25+
],
26+
targets: [
27+
.macro(
28+
name: "SwiftMacrosPlugin",
29+
dependencies: [
30+
.product(name: "SwiftSyntax", package: "swift-syntax"),
31+
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
32+
.product(name: "SwiftOperators", package: "swift-syntax"),
33+
.product(name: "SwiftParser", package: "swift-syntax"),
34+
.product(name: "SwiftParserDiagnostics", package: "swift-syntax"),
35+
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
36+
]
37+
),
38+
.target(
39+
name: "SwiftMacros",
40+
dependencies: [
41+
"SwiftMacrosPlugin"
42+
]
43+
),
44+
.executableTarget(
45+
name: "Examples",
46+
dependencies: [
47+
"SwiftMacros"
48+
]
49+
),
50+
.testTarget(
51+
name: "SwiftMacrosTests",
52+
dependencies: [
53+
"SwiftMacrosPlugin"
54+
]
55+
)
56+
]
57+
)
58+

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# SwiftMacros
2+
3+
🚧 A collection of useful macro definitions for Swift. (WIP)
4+
5+
6+
## Getting started
7+
8+
Just use the following dependency to your project.
9+
10+
```swift
11+
.package(url: "https://github.com/binarybirds/swift-macros", branch: "main"),
12+
```
13+
14+
Don't forget to add the `SwiftMacros` product to your target.
15+
```
16+
.product(name: "SwiftMacros", package: "swift-macros"),
17+
```
18+
19+
## Useful macros
20+
21+
### Init
22+
23+
Generates a public init for objects.
24+
25+
```swift
26+
import Foundation
27+
import SwiftMacros
28+
29+
@Init
30+
public struct Something: Codable {
31+
let foo: String
32+
let bar: Int
33+
let hello: Bool?
34+
35+
}
36+
```
37+
38+
That's all for now.

Sources/Examples/main.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Foundation
2+
import SwiftMacros
3+
4+
@Init
5+
public struct Something: Codable {
6+
let foo: String
7+
let bar: Int
8+
let hello: Bool?
9+
10+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Foundation
2+
3+
@attached(member, names: named(init))
4+
public macro Init() = #externalMacro(
5+
module: "SwiftMacrosPlugin",
6+
type: "InitMacro"
7+
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import SwiftSyntax
2+
import SwiftSyntaxMacros
3+
4+
public struct InitMacro: MemberMacro {
5+
6+
public static func expansion<Declaration, Context>(
7+
of node: AttributeSyntax,
8+
providingMembersOf declaration: Declaration,
9+
in context: Context
10+
) throws -> [SwiftSyntax.DeclSyntax] where Declaration : DeclGroupSyntax, Context : MacroExpansionContext {
11+
12+
let members = declaration.memberBlock.members
13+
14+
var props: [(name: String, type: String)] = []
15+
for member in members {
16+
guard
17+
let v = member.decl.as(VariableDeclSyntax.self),
18+
let b = v.bindings.first,
19+
let i = b.pattern.as(IdentifierPatternSyntax.self),
20+
let t = b.typeAnnotation?.type
21+
else {
22+
continue
23+
}
24+
let n = i.identifier.text
25+
let tv = t.description
26+
27+
props.append((name: n, type: tv))
28+
}
29+
30+
let parameters = props
31+
.map { "\($0.name): \($0.type)"}
32+
.joined(separator: ",\n")
33+
34+
let assignments = props
35+
.map { "self.\($0.name) = \($0.name)"}
36+
.joined(separator: "\n")
37+
38+
return [
39+
"""
40+
public init(
41+
\(raw: parameters)
42+
) {
43+
\(raw: assignments)
44+
}
45+
"""
46+
]
47+
}
48+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#if canImport(SwiftCompilerPlugin)
2+
import SwiftCompilerPlugin
3+
import SwiftSyntaxMacros
4+
5+
@main
6+
struct SwiftMacrosPlugin: CompilerPlugin {
7+
8+
let providingMacros: [Macro.Type] = [
9+
InitMacro.self,
10+
]
11+
}
12+
#endif
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import XCTest
2+
import SwiftSyntax
3+
import SwiftSyntaxBuilder
4+
import SwiftSyntaxMacros
5+
import SwiftMacrosPlugin
6+
7+
final class SwiftMacrosTests: XCTestCase {
8+
9+
let testMacros: [String: Macro.Type] = [
10+
"Init": InitMacro.self,
11+
]
12+
13+
func testInitMacro() throws {
14+
15+
let sf: SourceFileSyntax = """
16+
@Init
17+
public struct Something: Codable {
18+
let foo: String
19+
let bar: Int
20+
let hello: Bool?
21+
}
22+
"""
23+
24+
let expectation = """
25+
26+
public struct Something: Codable {
27+
let foo: String
28+
let bar: Int
29+
let hello: Bool?
30+
public init(
31+
foo: String,
32+
bar: Int,
33+
hello: Bool?
34+
) {
35+
self.foo = foo
36+
self.bar = bar
37+
self.hello = hello
38+
}
39+
}
40+
"""
41+
42+
let context = BasicMacroExpansionContext(
43+
sourceFiles: [
44+
sf: .init(
45+
moduleName: "TestModule",
46+
fullFilePath: "test.swift"
47+
)
48+
]
49+
)
50+
51+
let transformed = sf.expand(macros: testMacros, in: context)
52+
XCTAssertEqual(transformed.formatted().description, expectation)
53+
}
54+
}

0 commit comments

Comments
 (0)