Skip to content

Commit 3b827e2

Browse files
committed
-
1 parent 6d276e5 commit 3b827e2

File tree

14 files changed

+337
-58
lines changed

14 files changed

+337
-58
lines changed

.github/workflows/pull-request.yml

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,110 @@
11
name: Pull request
22
on: pull_request
33

4+
env:
5+
XCODE_VERSION: "16.3"
6+
47
jobs:
5-
pull-request:
8+
prepare:
69
runs-on: macos-15
7-
10+
outputs:
11+
platforms: ${{ steps.platforms.outputs.platforms }}
12+
scheme: ${{ steps.scheme.outputs.scheme }}
813
steps:
914
- uses: actions/checkout@v4
15+
- name: Setup Xcode
16+
run: sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app
1017

11-
- name: Setup
18+
- name: Setup mise
1219
env:
1320
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1421
run: |
1522
curl https://mise.run | sh
1623
mise install
17-
18-
- name: Lint
24+
- name: Run linters
1925
run: mise lint
2026

21-
- name: Build
27+
- name: Extract platforms
28+
id: platforms
29+
run: |
30+
platforms=$(swift package dump-package | jq -r '[.platforms[].platformName] | unique | @json')
31+
echo "Platforms: $platforms"
32+
echo "platforms=$platforms" >> $GITHUB_OUTPUT
33+
34+
- name: Extract scheme
35+
id: scheme
36+
run: |
37+
repo=$(basename $GITHUB_REPOSITORY)
38+
schemes=$(xcodebuild -list)
39+
echo "$schemes"
40+
41+
if echo "$schemes" | grep -q "$repo-Package"; then
42+
scheme="$repo-Package"
43+
elif echo "$schemes" | grep -q "$repo"; then
44+
scheme="$repo"
45+
else
46+
echo "Unable to select a scheme"
47+
exit 1
48+
fi
49+
50+
echo "Selected scheme: $scheme"
51+
echo "scheme=$scheme" >> $GITHUB_OUTPUT
52+
53+
build-and-test:
54+
needs: prepare
55+
runs-on: macos-15
56+
strategy:
57+
fail-fast: false
58+
matrix:
59+
platform: ${{ fromJSON(needs.prepare.outputs.platforms) }}
60+
steps:
61+
- uses: actions/checkout@v4
62+
- name: Setup Xcode
63+
run: sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app
64+
65+
- name: Map destinations
66+
if: ${{ matrix.platform != 'macos' }}
67+
id: destination
68+
run: |
69+
case "${{ matrix.platform }}" in
70+
ios)
71+
destination="platform=iOS Simulator,name=iPhone 16 Pro Max,OS=latest"
72+
;;
73+
maccatalyst)
74+
destination="platform=macOS,variant=Mac Catalyst"
75+
;;
76+
tvos)
77+
destination="platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=latest"
78+
;;
79+
visionos)
80+
destination="platform=visionOS Simulator,name=Apple Vision Pro,OS=latest"
81+
;;
82+
watchos)
83+
destination="platform=watchOS Simulator,name=Apple Watch Series 10 (46mm),OS=latest"
84+
;;
85+
*)
86+
echo "Unknown platform: ${{ matrix.platform }}"
87+
exit 1
88+
;;
89+
esac
90+
echo "destination=$destination" >> $GITHUB_OUTPUT
91+
92+
- name: Build (SPM)
93+
if: ${{ matrix.platform == 'macos' }}
2294
run: swift build
95+
- name: Build (Xcode)
96+
if: ${{ matrix.platform != 'macos' }}
97+
run: |
98+
xcodebuild build \
99+
-scheme ${{ needs.prepare.outputs.scheme }} \
100+
-destination "${{ steps.destination.outputs.destination }}"
23101
24-
- name: Test
25-
run: swift test
102+
- name: Test (SPM)
103+
if: ${{ matrix.platform == 'macos' }}
104+
run: swift test
105+
- name: Test (Xcode)
106+
if: ${{ matrix.platform != 'macos' }}
107+
run: |
108+
xcodebuild test \
109+
-scheme ${{ needs.prepare.outputs.scheme }} \
110+
-destination "${{ steps.destination.outputs.destination }}"

.github/workflows/release.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ on:
88
jobs:
99
release:
1010
runs-on: ubuntu-latest
11-
1211
steps:
1312
- name: Draft release
1413
env:
@@ -18,4 +17,4 @@ jobs:
1817
gh release create "$TAG_NAME" \
1918
--repo="$GITHUB_REPOSITORY" \
2019
--generate-notes \
21-
--draft
20+
--draft

.swiftlint.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ file_length:
166166
identifier_name:
167167
excluded: [id, x, y, z]
168168

169+
line_length:
170+
ignores_comments: true
171+
169172
nesting:
170173
type_level: 2
171174

Package.resolved

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

Package.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
// swift-tools-version: 6.0
1+
// swift-tools-version: 6.1
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
55

66
let package = Package(
77
name: "PrincipleMacros",
88
platforms: [
9-
.macOS(.v13),
10-
.macCatalyst(.v16),
11-
.iOS(.v16),
12-
.tvOS(.v16),
13-
.watchOS(.v9),
14-
.visionOS(.v1)
9+
.macOS(.v15),
10+
.macCatalyst(.v18),
11+
.iOS(.v18),
12+
.tvOS(.v18),
13+
.watchOS(.v11),
14+
.visionOS(.v2)
1515
],
1616
products: [
1717
.library(
@@ -22,7 +22,7 @@ let package = Package(
2222
dependencies: [
2323
.package(
2424
url: "https://github.com/NSFatalError/Principle",
25-
from: "0.0.1"
25+
from: "1.0.0"
2626
),
2727
.package(
2828
url: "https://github.com/swiftlang/swift-syntax",

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
11
# PrincipleMacros
2+
3+
![Swift](https://img.shields.io/badge/Swift-6.0-EF5239?logo=swift&labelColor=white)
4+
5+
Essential tools that extend the capabilities of `SwiftSyntax`, simplifying the implementation of custom macros.
6+
7+
> [!WARNING]
8+
> This package is considered an implementation detail of some of my other open-source projects.
9+
> While it follows semantic versioning rules and has decent test coverage, it is currently undocumented,
10+
> and future releases may introduce multiple breaking changes. Use it at your own discretion.
11+
12+
## Installation
13+
14+
```swift
15+
.package(
16+
url: "https://github.com/NSFatalError/PrincipleMacros",
17+
from: "1.0.0"
18+
)
19+
```

Sources/PrincipleMacros/Builders/Expressions/SwitchExprBuilder.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,21 @@ public struct SwitchExprBuilder: ExprBuilder {
2525
}
2626

2727
public func build() -> SwitchExprSyntax {
28-
SwitchExprSyntax(subject: subject, cases: switchCases())
28+
SwitchExprSyntax(
29+
subject: subject.withLeadingSpace.withTrailingSpace,
30+
leftBrace: .leftBraceToken().withTrailingNewline,
31+
cases: switchCases()
32+
)
2933
}
3034

3135
private func switchCases() -> SwitchCaseListSyntax {
3236
SwitchCaseListSyntax(
3337
cases.map { enumCase in
3438
.switchCase(
35-
SwitchCaseSyntax(
36-
"case \(switchCase(for: enumCase)): \(statementsBuilder(enumCase))"
37-
)
39+
SwitchCaseSyntax("""
40+
case \(switchCase(for: enumCase)):
41+
\(statementsBuilder(enumCase))\n
42+
""")
3843
)
3944
}
4045
)
@@ -60,7 +65,7 @@ public struct SwitchExprBuilder: ExprBuilder {
6065

6166
return SwitchCaseItemSyntax(
6267
pattern: ValueBindingPatternSyntax(
63-
bindingSpecifier: .keyword(.let),
68+
bindingSpecifier: .keyword(.let).withTrailingSpace,
6469
pattern: ExpressionPatternSyntax(
6570
expression: FunctionCallExprSyntax(
6671
calledExpression: memberAccessExpression,

Sources/PrincipleMacros/Imports.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
// Copyright © 2025 Kamil Strzelecki. All rights reserved.
77
//
88

9-
@_documentation(visibility: internal) @_exported import SwiftBasicFormat
10-
@_documentation(visibility: internal) @_exported import SwiftDiagnostics
11-
@_documentation(visibility: internal) @_exported import SwiftSyntax
12-
@_documentation(visibility: internal) @_exported import SwiftSyntaxBuilder
13-
@_documentation(visibility: internal) @_exported import SwiftSyntaxMacros
9+
@_documentation(visibility: private) @_exported import SwiftBasicFormat
10+
@_documentation(visibility: private) @_exported import SwiftDiagnostics
11+
@_documentation(visibility: private) @_exported import SwiftSyntax
12+
@_documentation(visibility: private) @_exported import SwiftSyntaxBuilder
13+
@_documentation(visibility: private) @_exported import SwiftSyntaxMacros
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// EnumCaseCallExprBuilderTests.swift
3+
// PrincipleMacros
4+
//
5+
// Created by Kamil Strzelecki on 05/04/2025.
6+
// Copyright © 2025 Kamil Strzelecki. All rights reserved.
7+
//
8+
9+
@testable import PrincipleMacros
10+
import Testing
11+
12+
internal struct EnumCaseCallExprBuilderTests {
13+
14+
func makeEnumCase(from decl: DeclSyntax) throws -> EnumCase {
15+
let enumCaseDecl = try #require(EnumCaseDeclSyntax(decl))
16+
let enumElement = try #require(enumCaseDecl.elements.first)
17+
return EnumCase(declaration: enumCaseDecl, element: enumElement)
18+
}
19+
20+
@Test
21+
func testCallWithoutAssociatedValues() throws {
22+
let enumCase = try makeEnumCase(from: "case first")
23+
let builder = EnumCaseCallExprBuilder(for: enumCase) { _ in
24+
Issue.record()
25+
return "" as ExprSyntax
26+
}
27+
#expect(builder.build().description == ".first")
28+
}
29+
30+
@Test
31+
func testCallWithUnnamedAssociatedValue() throws {
32+
let enumCase = try makeEnumCase(from: "case second(Int)")
33+
let builder = EnumCaseCallExprBuilder(for: enumCase) { _ in
34+
"123" as ExprSyntax
35+
}
36+
#expect(builder.build().description == ".second(123)")
37+
}
38+
39+
@Test
40+
func testCallWithMultipleAssociatedValues() throws {
41+
let enumCase = try makeEnumCase(from: "case third(arg: String, Int)")
42+
let builder = EnumCaseCallExprBuilder(for: enumCase) { associatedValue in
43+
if associatedValue.standardizedName.description == "arg" {
44+
"argument" as ExprSyntax
45+
} else {
46+
"123" as ExprSyntax
47+
}
48+
}
49+
#expect(builder.build().description == ".third(arg: argument, 123)")
50+
}
51+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// SwitchExprBuilderTests.swift
3+
// PrincipleMacros
4+
//
5+
// Created by Kamil Strzelecki on 05/04/2025.
6+
// Copyright © 2025 Kamil Strzelecki. All rights reserved.
7+
//
8+
9+
@testable import PrincipleMacros
10+
import Testing
11+
12+
internal struct SwitchExprBuilderTests {
13+
14+
func makeEnumCase(from decl: DeclSyntax) throws -> EnumCase {
15+
let enumCaseDecl = try #require(EnumCaseDeclSyntax(decl))
16+
let enumElement = try #require(decl.elements.first)
17+
return EnumCase(declaration: enumCaseDecl, element: enumElement)
18+
}
19+
20+
@Test
21+
func testSwitchExpression() throws {
22+
let enumCases = try EnumCasesList([
23+
makeEnumCase(from: "case first"),
24+
makeEnumCase(from: "case second(Int)"),
25+
makeEnumCase(from: "case third(arg: String, Int)")
26+
])
27+
28+
let builder = SwitchExprBuilder(for: enumCases, over: "subject") { enumCase in
29+
"return \(raw: enumCase.associatedValues.count)"
30+
}
31+
32+
let expectation = """
33+
switch subject {
34+
case .first:
35+
return 0
36+
case let .second(_0):
37+
return 1
38+
case let .third(arg, _1):
39+
return 2
40+
}
41+
"""
42+
43+
#expect(builder.build().description == expectation)
44+
}
45+
}

0 commit comments

Comments
 (0)