Skip to content

Commit f3c3d13

Browse files
authored
Docs (#4)
1 parent db8a49d commit f3c3d13

File tree

14 files changed

+371
-63
lines changed

14 files changed

+371
-63
lines changed

.github/workflows/pull-request.yml

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,116 @@
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+
set -o pipefail
99+
xcodebuild build \
100+
-scheme ${{ needs.prepare.outputs.scheme }} \
101+
-destination "${{ steps.destination.outputs.destination }}" | \
102+
xcbeautify --renderer github-actions
23103
24-
- name: Test
25-
run: swift test
104+
- name: Test (SPM)
105+
if: ${{ matrix.platform == 'macos' }}
106+
run: |
107+
set -o pipefail
108+
swift test | xcbeautify --renderer github-actions
109+
- name: Test (Xcode)
110+
if: ${{ matrix.platform != 'macos' }}
111+
run: |
112+
set -o pipefail
113+
xcodebuild test \
114+
-scheme ${{ needs.prepare.outputs.scheme }} \
115+
-destination "${{ steps.destination.outputs.destination }}" | \
116+
xcbeautify --renderer github-actions

.github/workflows/release.yml

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,43 @@
11
name: Release
2-
32
on:
43
push:
54
tags:
65
- '*'
76

7+
env:
8+
XCODE_VERSION: "16.3"
9+
810
jobs:
911
release:
10-
runs-on: ubuntu-latest
11-
12+
runs-on: macos-15
1213
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
- name: Setup Xcode
18+
run: sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app
19+
20+
- name: Verify changes
21+
run: |
22+
current_tag=${GITHUB_REF#refs/tags/}
23+
previous_tag=$(git tag --sort=-v:refname | head -n 2 | tail -n 1)
24+
25+
current_major=$(echo "$current_tag" | cut -d '.' -f 1)
26+
previous_major=$(echo "$previous_tag" | cut -d '.' -f 1)
27+
echo "Comparing $current_tag with $previous_tag..."
28+
29+
if [ "$current_major" -gt "$previous_major" ]; then
30+
swift package diagnose-api-breaking-changes "$previous_tag" || true
31+
else
32+
swift package diagnose-api-breaking-changes "$previous_tag"
33+
fi
34+
1335
- name: Draft release
1436
env:
1537
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1638
run: |
17-
TAG_NAME=${GITHUB_REF#refs/tags/}
18-
gh release create "$TAG_NAME" \
39+
current_tag=${GITHUB_REF#refs/tags/}
40+
gh release create "$current_tag" \
1941
--repo="$GITHUB_REPOSITORY" \
2042
--generate-notes \
21-
--draft
43+
--draft

.swiftlint.yml

Lines changed: 4 additions & 1 deletion
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

@@ -192,4 +195,4 @@ custom_rules:
192195
empty_line_after_type_declaration:
193196
name: "Empty line after type declaration"
194197
message: "Type declaration should start with an empty line."
195-
regex: "( |^)(actor|class|struct|enum|protocol|extension) (?!var)[^\\{]*? \\{(?!\\n*\\}) *\\n? *\\S"
198+
regex: "( |^)(actor|class|struct|enum|protocol|extension) (?!var)[^\\{]*? \\{(?!\\s*\\}) *\\n? *\\S"

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-
@_exported import SwiftBasicFormat
10-
@_exported import SwiftDiagnostics
11-
@_exported import SwiftSyntax
12-
@_exported import SwiftSyntaxBuilder
13-
@_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+
}

0 commit comments

Comments
 (0)