Skip to content

Commit

Permalink
Add @apiservice Macro (#41)
Browse files Browse the repository at this point in the history
* #35 Adding  RestAPI macro

* #35: Add missing names parameter for @attached extension macro declaration; remove unnecessary @attached member declaration; remove macro without baseURL paramater

* #35: Rename @RESTapi to @apiservice

* Update test.yml for swift 5.9

* Update test.yml

* Add macros target as test dependency

* #35: Temporarily disable macros tests on windows

* #35 temporarily disable building macros on windows when testing

* #35: Disable macros on windows during tests

* #35: Don't use macros in regular unit tests

* Update test.yml to set Xcode 15.1 on macOS 13 runner

* Update test.yml - select swift version

* Update test.yml

* Update test.yml

* Update test.yml

* Update test.yml

* #35: Complete macro renaming

* #35: Add error handling for @apiservice macro

* #35: Maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* #35: maintain support for swift <5.9

* Report swift version in ci action

* #35: Remove  unnecessary import check
  • Loading branch information
tdeleon authored Jan 3, 2024
1 parent 80e780f commit d99ee4b
Show file tree
Hide file tree
Showing 13 changed files with 451 additions and 12 deletions.
24 changes: 17 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
name: Test
name: Build & Test

on: [push]

jobs:
build:
strategy:
max-parallel: 5
fail-fast: false
matrix:
os: [macos-12, macos-latest, ubuntu-latest, windows-latest]
os: [macos-13, ubuntu-latest, windows-latest]
swift: ["5.7", "5.8", "5.9"]
runs-on: ${{ matrix.os }}

steps:
- if: runner.os == 'Windows'
name: Setup Swift
- if: ${{ (runner.os != 'Windows') || (runner.os == 'Windows' && matrix.swift != '5.7') }}
name: Setup Swift Version
uses: SwiftyLab/setup-swift@latest
with:
swift-version: ${{ matrix.swift }}

- if: ${{ (runner.os == 'Windows') && (matrix.swift == '5.7') }}
name: Setup Swift Version (5.7 on Windows)
uses: compnerd/gha-setup-swift@main
with:
branch: swift-5.7-release
tag: 5.7-RELEASE


- name: Get Swift Version
run: swift --version

- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Build
run: swift build

- name: Test
run: swift test

116 changes: 116 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/Relax-Package.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Relax"
BuildableName = "Relax"
BlueprintName = "Relax"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "URLMock"
BuildableName = "URLMock"
BlueprintName = "URLMock"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "RelaxTests"
BuildableName = "RelaxTests"
BlueprintName = "RelaxTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "RelaxTests"
BuildableName = "RelaxTests"
BlueprintName = "RelaxTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "RelaxMacrosTests"
BuildableName = "RelaxMacrosTests"
BlueprintName = "RelaxMacrosTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Relax"
BuildableName = "Relax"
BlueprintName = "Relax"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
66 changes: 66 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/URLMock.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "URLMock"
BuildableName = "URLMock"
BlueprintName = "URLMock"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "URLMock"
BuildableName = "URLMock"
BlueprintName = "URLMock"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
14 changes: 14 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"pins" : [
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
"version" : "509.0.2"
}
}
],
"version" : 2
}
66 changes: 66 additions & 0 deletions [email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
import CompilerPluginSupport


var targets: [Target] = [
.target(
name: "URLMock",
dependencies: ["Relax"]
),
.testTarget(
name: "RelaxTests",
dependencies: ["Relax", "URLMock"]
),
]

var dependencies = [Package.Dependency]()

// Macros do not currently compile on windows when building tests: https://github.com/apple/swift-package-manager/issues/7174
#if canImport(XCTest) && os(Windows)
targets.append(.target(name: "Relax"))
#else
targets.append(
contentsOf: [
.target(name: "Relax", dependencies: ["RelaxMacros"]),
.macro(
name: "RelaxMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
.testTarget(
name: "RelaxMacrosTests",
dependencies: ["RelaxMacros", .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax")]
)
]
)
dependencies = [
// Depend on the Swift 5.9 release of SwiftSyntax
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"),
]
#endif

let package = Package(
name: "Relax",
platforms: [
.iOS(.v14),
.tvOS(.v14),
.watchOS(.v7),
.macOS(.v12),
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "Relax",
targets: ["Relax"]),
.library(
name: "URLMock",
targets: ["URLMock"])
],
dependencies: dependencies,
targets: targets
)
13 changes: 13 additions & 0 deletions Sources/Relax/Macros/APIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// APIService.swift
//
//
// Created by Thomas De Leon on 12/27/23.
//

#if swift(>=5.9)
import Foundation

@attached(extension, conformances: APIComponent, names: named(baseURL))
public macro APIService(_ baseURL: String) = #externalMacro(module: "RelaxMacros", type: "APIServiceMacro")
#endif
51 changes: 51 additions & 0 deletions Sources/RelaxMacros/APIServiceMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// APIServiceMacro.swift
//
//
// Created by Thomas De Leon on 12/27/23.
//

import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
import SwiftDiagnostics
import Foundation

public struct APIServiceMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
guard let urlString = node
.arguments?.as(LabeledExprListSyntax.self)?
.first?.expression.as(StringLiteralExprSyntax.self)?
.segments
.first?.trimmedDescription else {
let error = Diagnostic(node: node, message: RelaxMacroDiagnostic.invalidBaseURL)
context.diagnose(error)
return []
}

// check that the URL will be valid
guard URL(string: urlString) != nil else {
let error = Diagnostic(node: node, message: RelaxMacroDiagnostic.invalidBaseURL)
context.diagnose(error)
return []
}

let decl: DeclSyntax =
"""
extension \(type.trimmed): APIComponent {
static let baseURL: URL = URL(string: \"\(raw: urlString)\")!
}
"""

guard let extensionDecl = decl.as(ExtensionDeclSyntax.self) else {
return []
}
return [extensionDecl]
}
}
Loading

0 comments on commit d99ee4b

Please sign in to comment.