-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from surfstudio/SNP-1649-url-macro
- Loading branch information
Showing
7 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: Unit Tests | ||
|
||
on: [push] | ||
|
||
jobs: | ||
test: | ||
runs-on: macos-latest | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Swift | ||
uses: swift-actions/setup-swift@v2 | ||
with: | ||
swift-version: '5.9' | ||
|
||
- name: Build | ||
run: swift build | ||
|
||
- name: Test | ||
run: swift test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import Foundation | ||
import SurfMacrosSupport | ||
import SwiftCompilerPlugin | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
public enum URLMacro: ExpressionMacro { | ||
|
||
// MARK: - Names | ||
|
||
private enum Names { | ||
static let url = "URL" | ||
static let argument = "string" | ||
} | ||
|
||
public static func expansion( | ||
of node: some FreestandingMacroExpansionSyntax, | ||
in context: some MacroExpansionContext | ||
) throws -> ExprSyntax { | ||
let (argument, value) = try getInputArgument(from: node) | ||
try checkURLString(value, from: argument) | ||
return .init(createURLExpression(with: argument)) | ||
} | ||
|
||
} | ||
|
||
// MARK: - Getters | ||
|
||
private extension URLMacro { | ||
|
||
static func getInputArgument( | ||
from node: some FreestandingMacroExpansionSyntax | ||
) throws -> (argument: ExprSyntax, argumentValue: String) { | ||
guard | ||
let argument = node.argumentList.first?.expression, | ||
let segments = argument.as(StringLiteralExprSyntax.self)?.segments, | ||
segments.count == 1, | ||
case .stringSegment(let literalSegment)? = segments.first | ||
else { | ||
throw CustomError(description: "#URL requires a static string literal") | ||
} | ||
return (argument: argument, argumentValue: literalSegment.content.text) | ||
} | ||
|
||
} | ||
|
||
// MARK: - Checks | ||
|
||
private extension URLMacro { | ||
|
||
static func checkURLString(_ urlString: String, from argument: ExprSyntax) throws { | ||
guard let _ = URL(string: urlString) else { | ||
throw CustomError(description: "malformed url: \(argument)") | ||
} | ||
} | ||
|
||
} | ||
|
||
// MARK: - Creations | ||
|
||
private extension URLMacro { | ||
|
||
static func createURLExpression(with argument: ExprSyntax) -> ExprSyntaxProtocol { | ||
let urlInit = DeclReferenceExprSyntax(baseName: .identifier(Names.url)) | ||
let stringArgument = LabeledExprSyntax( | ||
label: .identifier(Names.argument), | ||
expression: argument | ||
) | ||
let functionCall = FunctionCallExprSyntax( | ||
calledExpression: urlInit, | ||
arguments: [stringArgument] | ||
) | ||
return ForceUnwrapExprSyntax(expression: functionCall) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import SwiftCompilerPlugin | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
struct UtilsPlugin { | ||
static let providingMacros: [Macro.Type] = [ | ||
URLMacro.self | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import Foundation | ||
|
||
/// Check if provided string literal is a valid URL and produce a non-optional | ||
/// URL value. Emit error otherwise. | ||
@freestanding(expression) | ||
public macro URL(_ stringLiteral: String) -> URL = #externalMacro(module: "SurfMacroBody", type: "URLMacro") |
18 changes: 18 additions & 0 deletions
18
Sources/SurfMacros/Support/Library/Errors/CustomError.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// | ||
// CustomError.swift | ||
// | ||
// | ||
// Created by pavlov on 30.06.2024. | ||
// | ||
|
||
import Foundation | ||
|
||
public struct CustomError: Error, CustomStringConvertible { | ||
|
||
public let description: String | ||
|
||
public init(description: String) { | ||
self.description = description | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import SwiftSyntaxMacros | ||
import SwiftSyntaxMacrosTestSupport | ||
import XCTest | ||
|
||
#if canImport(SurfMacroBody) | ||
import SurfMacroBody | ||
|
||
private let testMacros: [String: Macro.Type] = ["URL": URLMacro.self] | ||
#endif | ||
|
||
final class URLMacroTests: XCTestCase { | ||
func testExpansionWithMalformedURLEmitsError() { | ||
assertMacroExpansion( | ||
""" | ||
let invalid = #URL("https://not a url.com") | ||
""", | ||
expandedSource: """ | ||
let invalid = #URL("https://not a url.com") | ||
""", | ||
diagnostics: [ | ||
.init(message: #"malformed url: "https://not a url.com""#, line: 1, column: 15, severity: .error) | ||
], | ||
macros: testMacros | ||
) | ||
} | ||
|
||
func testExpansionWithStringInterpolationEmitsError() { | ||
assertMacroExpansion( | ||
#""" | ||
#URL("https://\(domain)/api/path") | ||
"""#, | ||
expandedSource: #""" | ||
#URL("https://\(domain)/api/path") | ||
"""#, | ||
diagnostics: [ | ||
.init(message: "#URL requires a static string literal", line: 1, column: 1, severity: .error) | ||
], | ||
macros: testMacros | ||
) | ||
} | ||
|
||
func testExpansionWithValidURL() { | ||
assertMacroExpansion( | ||
""" | ||
let valid = #URL("https://swift.org/") | ||
""", | ||
expandedSource: """ | ||
let valid = URL(string: "https://swift.org/")! | ||
""", | ||
macros: testMacros | ||
) | ||
} | ||
|
||
} |