Skip to content

Commit 7a087de

Browse files
committed
task: Swift URL macro support
1 parent 3021f28 commit 7a087de

5 files changed

Lines changed: 109 additions & 2 deletions

File tree

Package.resolved

Lines changed: 9 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: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
// swift-tools-version:5.7
1+
// swift-tools-version:5.9
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
5+
import CompilerPluginSupport
56

67
let package = Package(
78
name: "GoodExtensions",
89
platforms: [
10+
.macOS(.v12),
911
.iOS(.v13)
1012
],
1113
products: [
@@ -22,10 +24,15 @@ let package = Package(
2224
name: "GoodCombineExtensions",
2325
targets: ["GoodCombineExtensions"]
2426
),
27+
.library(
28+
name: "GoodMacros",
29+
targets: ["GoodMacros"]
30+
)
2531
],
2632
dependencies: [
2733
// Dependencies declare other packages that this package depends on.
28-
.package(url: "https://github.com/CombineCommunity/CombineExt.git", from: "1.0.0")
34+
.package(url: "https://github.com/CombineCommunity/CombineExt.git", from: "1.0.0"),
35+
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.2")
2936
],
3037
targets: [
3138
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
@@ -50,6 +57,19 @@ let package = Package(
5057
dependencies: [],
5158
path: "./Sources/GoodStructs"
5259
),
60+
.target(
61+
name: "GoodMacros",
62+
dependencies: ["MacroCollection"],
63+
path: "./Sources/GoodMacros"
64+
),
65+
.macro(
66+
name: "MacroCollection",
67+
dependencies: [
68+
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
69+
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
70+
],
71+
path: "./Sources/MacroCollection"
72+
),
5373
.testTarget(
5474
name: "GoodExtensionsTests",
5575
dependencies: ["GoodExtensions"],
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// GoodMacros.swift
3+
// GoodExtensions-iOS
4+
//
5+
// Created by Filip Šašala on 04/04/2024.
6+
//
7+
8+
import Foundation
9+
10+
@freestanding(expression)
11+
public macro URL(_ string: String) -> URL = #externalMacro(module: "MacroCollection", type: "URLMacro")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// MacroCollection.swift
3+
// GoodExtensions-iOS
4+
//
5+
// Created by Filip Šašala on 04/04/2024.
6+
//
7+
8+
import Foundation
9+
import SwiftCompilerPlugin
10+
import SwiftSyntaxMacros
11+
12+
@main struct Plugins: CompilerPlugin {
13+
14+
let providingMacros: [Macro.Type] = [
15+
URLMacro.self
16+
]
17+
18+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// URLMacro.swift
3+
// GoodExtensions-iOS
4+
//
5+
// Created by Filip Šašala on 04/04/2024.
6+
//
7+
8+
import Foundation
9+
import SwiftSyntax
10+
import SwiftSyntaxMacros
11+
12+
public struct URLMacro: ExpressionMacro {
13+
14+
public static func expansion(
15+
of node: some FreestandingMacroExpansionSyntax,
16+
in context: some MacroExpansionContext
17+
) throws -> ExprSyntax {
18+
guard let argument = node.argumentList.first?.expression,
19+
let segments = argument.as(StringLiteralExprSyntax.self)?.segments,
20+
segments.count == 1,
21+
case .stringSegment(let literalSegment)? = segments.first
22+
else {
23+
throw URLMacroError.requiresStaticStringLiteral
24+
}
25+
guard URL(string: literalSegment.content.text) != nil else {
26+
throw URLMacroError.malformedURL(urlString: "\(argument)")
27+
}
28+
29+
return "URL(string: \(argument))!"
30+
}
31+
32+
}
33+
34+
enum URLMacroError: Error, CustomStringConvertible {
35+
36+
case requiresStaticStringLiteral
37+
case malformedURL(urlString: String)
38+
39+
var description: String {
40+
switch self {
41+
case .requiresStaticStringLiteral:
42+
"#URL macro requires a static string literal"
43+
44+
case .malformedURL(let urlString):
45+
"URL is malformed: \(urlString)"
46+
}
47+
}
48+
49+
}

0 commit comments

Comments
 (0)