-
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.
- Loading branch information
1 parent
a2577e8
commit 8ac9067
Showing
11 changed files
with
712 additions
and
365 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,11 @@ | ||
// | ||
// Copyright © Surf. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Замыкание без параметра | ||
public typealias EmptyClosure = () -> Void | ||
|
||
/// Замыкание с одним параметром | ||
public typealias Closure<T> = (T) -> Void |
133 changes: 133 additions & 0 deletions
133
Sources/SurfMacros/Implementation/Signals/CompletionHandlerMacro.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,133 @@ | ||
import SwiftCompilerPlugin | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
import SurfMacrosSupport | ||
|
||
public struct CompletionHandlerMacro: PeerMacro { | ||
|
||
// MARK: - Names | ||
|
||
private enum Names { | ||
static let completion = "completion" | ||
static let emptyClosure = "EmptyClosure" | ||
|
||
static var `protocol` = "" | ||
|
||
static var `class`: String { | ||
return `protocol` + "Handler" | ||
} | ||
|
||
} | ||
|
||
// MARK: - Macro | ||
|
||
public static func expansion( | ||
of node: AttributeSyntax, | ||
providingPeersOf declaration: some DeclSyntaxProtocol, | ||
in context: some MacroExpansionContext | ||
) throws -> [DeclSyntax] { | ||
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else { | ||
throw DeclarationError.wrongAttaching(expected: .protocol) | ||
} | ||
Names.protocol = protocolDecl.name.text | ||
try SignalsMacroGroupSupport.checkProtocolDeclaration(protocolDecl) | ||
let protocolFuncDecls = protocolDecl.memberBlock.functionDecls | ||
let handlerClass = createHandlerClass(with: protocolFuncDecls) | ||
return [.init(handlerClass)] | ||
} | ||
|
||
} | ||
|
||
// MARK: - Creations | ||
|
||
private extension CompletionHandlerMacro { | ||
|
||
static func createHandlerClass(with protocolFuncDecls: [FunctionDeclSyntax]) -> ClassDeclSyntax { | ||
let privateModifier = DeclModifierSyntax(name: .keyword(.private)) | ||
let protocolIdentifier = createProtocolIdentifier() | ||
let memberBlock = createMemberBlock(with: protocolFuncDecls) | ||
return .init( | ||
modifiers: [privateModifier], | ||
name: .identifier(Names.class), | ||
inheritanceClause: .init(inheritedTypes: [.init(type: protocolIdentifier)]), | ||
memberBlock: memberBlock | ||
) | ||
} | ||
|
||
static func createProtocolIdentifier() -> IdentifierTypeSyntax { | ||
return .init(name: .identifier(Names.protocol)) | ||
} | ||
|
||
static func createMemberBlock(with protocolFuncDecls: [FunctionDeclSyntax]) -> MemberBlockSyntax { | ||
let itemList = MemberBlockItemListSyntax { | ||
createCompletionProperty() | ||
createInit() | ||
for funcDecl in protocolFuncDecls { | ||
SignalsMacroGroupSupport.createFuncDecl( | ||
from: funcDecl, | ||
with: createFuncBody() | ||
) | ||
} | ||
} | ||
return .init(members: itemList) | ||
} | ||
|
||
static func createCompletionProperty() -> VariableDeclSyntax { | ||
let privateModifier = DeclModifierSyntax(name: .keyword(.private)) | ||
let type = createCompletionType() | ||
let patternBinding = PatternBindingSyntax( | ||
pattern: IdentifierPatternSyntax(identifier: .identifier(Names.completion)), | ||
typeAnnotation: .init(type: type) | ||
) | ||
return .init( | ||
modifiers: [privateModifier], | ||
bindingSpecifier: .keyword(.let), | ||
bindings: [patternBinding] | ||
) | ||
} | ||
|
||
static func createInit() -> InitializerDeclSyntax { | ||
let signature = createInitSignature() | ||
let body = createInitBody() | ||
return .init(signature: signature, body: body) | ||
} | ||
|
||
static func createInitSignature() -> FunctionSignatureSyntax { | ||
let type = createCompletionType() | ||
let defaultNil = InitializerClauseSyntax(value: NilLiteralExprSyntax()) | ||
let completionParameter = FunctionParameterSyntax( | ||
firstName: .identifier(Names.completion), | ||
type: type, | ||
defaultValue: defaultNil | ||
) | ||
return .init(parameterClause: .init(parameters: [completionParameter])) | ||
} | ||
|
||
static func createInitBody() -> CodeBlockSyntax { | ||
let selfCompletion = MemberAccessExprSyntax( | ||
base: DeclReferenceExprSyntax(baseName: .keyword(.`self`)), | ||
declName: .init(baseName: .identifier(Names.completion)) | ||
) | ||
let completion = DeclReferenceExprSyntax(baseName: .identifier(Names.completion)) | ||
let assigmentOperator = InfixOperatorExprSyntax( | ||
leftOperand: selfCompletion, | ||
operator: AssignmentExprSyntax(), | ||
rightOperand: completion | ||
) | ||
return .init(statements: [.init(item: .expr(.init(assigmentOperator)))]) | ||
} | ||
|
||
static func createCompletionType() -> TypeSyntaxProtocol { | ||
return OptionalTypeSyntax(wrappedType: IdentifierTypeSyntax(name: .identifier(Names.emptyClosure))) | ||
} | ||
|
||
static func createFuncBody() -> CodeBlockSyntax { | ||
let completion = DeclReferenceExprSyntax(baseName: .identifier(Names.completion)) | ||
let completionCall = FunctionCallExprSyntax( | ||
calledExpression: OptionalChainingExprSyntax(expression: completion) | ||
) | ||
return .init(statements: [.init(item: .expr(.init(completionCall)))]) | ||
} | ||
|
||
} |
23 changes: 0 additions & 23 deletions
23
Sources/SurfMacros/Implementation/Signals/MulticastError.swift
This file was deleted.
Oops, something went wrong.
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
82 changes: 82 additions & 0 deletions
82
Sources/SurfMacros/Implementation/Signals/Support/SignalsMacroGroupSupport.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,82 @@ | ||
// | ||
// SignalsMacroGroupSupport.swift | ||
// | ||
// | ||
// Created by pavlov on 01.07.2024. | ||
// | ||
|
||
import SwiftCompilerPlugin | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
import SurfMacrosSupport | ||
|
||
struct SignalsMacroGroupSupport {} | ||
|
||
// MARK: - Checks | ||
|
||
extension SignalsMacroGroupSupport { | ||
|
||
static func checkProtocolDeclaration(_ declaration: ProtocolDeclSyntax) throws { | ||
try checkMembers(of: declaration) | ||
} | ||
|
||
} | ||
|
||
// MARK: - Creations | ||
|
||
extension SignalsMacroGroupSupport { | ||
|
||
static func createFuncDecl( | ||
from protocolFuncDecl: FunctionDeclSyntax, | ||
with body: CodeBlockSyntax, | ||
modifiers: DeclModifierListSyntax = [] | ||
) -> FunctionDeclSyntax { | ||
var funcDecl = protocolFuncDecl.trimmed | ||
funcDecl.body = body | ||
funcDecl.modifiers = modifiers | ||
return funcDecl | ||
} | ||
|
||
} | ||
|
||
// MARK: - Private Methods | ||
|
||
private extension SignalsMacroGroupSupport { | ||
|
||
static func checkMembers(of decl: ProtocolDeclSyntax) throws { | ||
try decl.memberBlock.members.forEach { member in | ||
if let funcDecl = member.decl.as(FunctionDeclSyntax.self), | ||
!isAppropriateFuncDecl(funcDecl) { | ||
throw CustomError( | ||
description: """ | ||
The only allowed format of a function is the following: | ||
func <name>(<argument>, ...) | ||
""" | ||
) | ||
} else if member.decl.is(VariableDeclSyntax.self) { | ||
throw DeclarationError.unexpectedVariable | ||
} else if member.decl.is(AssociatedTypeDeclSyntax.self) { | ||
throw DeclarationError.unexpectedAssociatedType | ||
} | ||
} | ||
} | ||
|
||
static func isAppropriateFuncDecl(_ decl: FunctionDeclSyntax) -> Bool { | ||
if !decl.modifiers.contains(where: isStatic), | ||
case .identifier = decl.name.tokenKind, | ||
decl.signature.returnClause == nil, | ||
decl.signature.effectSpecifiers == nil, | ||
decl.genericWhereClause == nil, | ||
decl.genericParameterClause == nil, | ||
decl.attributes.isEmpty { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
static func isStatic(_ modifier: DeclModifierSyntax) -> Bool { | ||
modifier.name.tokenKind == .keyword(.static) | ||
} | ||
|
||
} |
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,2 @@ | ||
@attached(peer, names: suffixed(Handler)) | ||
public macro CompletionHandler() = #externalMacro(module: "SurfMacroBody", type: "CompletionHandlerMacro") |
Oops, something went wrong.