-
Notifications
You must be signed in to change notification settings - Fork 1
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
Showing
16 changed files
with
622 additions
and
422 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
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 |
---|---|---|
|
@@ -2,7 +2,6 @@ | |
// Copyright © 2024 Sage. | ||
// All Rights Reserved. | ||
|
||
|
||
import Foundation | ||
|
||
@attached(peer) | ||
|
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
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
103 changes: 103 additions & 0 deletions
103
Sources/SageSwiftKitMacros/MockableMacros/AutoMockableBuilder.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,103 @@ | ||
// | ||
// Copyright © 2024 Sage. | ||
// All Rights Reserved. | ||
|
||
import Foundation | ||
import SwiftSyntax | ||
import SwiftSyntaxMacros | ||
import SwiftSyntaxBuilder | ||
|
||
public enum AutoMockable: PeerMacro { | ||
static var mocksVarName: String { "mock" } | ||
|
||
public static func expansion( | ||
of node: AttributeSyntax, | ||
providingPeersOf declaration: some DeclSyntaxProtocol, | ||
in context: some MacroExpansionContext | ||
) throws -> [DeclSyntax] { | ||
guard let protocolSyntax = declaration.as(ProtocolDeclSyntax.self) else { | ||
return [] | ||
} | ||
|
||
let accessLevel = node | ||
.adapter | ||
.findArgument(id: "accessLevel")? | ||
.adapter | ||
.expression(cast: StringLiteralExprSyntax.self)?.representedLiteralValue ?? "internal" | ||
|
||
let procotolName = protocolSyntax.name.text | ||
|
||
guard let members = declaration.as(ProtocolDeclSyntax.self)?.memberBlock.members else { | ||
return [] | ||
} | ||
|
||
let variablesToMock: [VariableDeclSyntax] = members.compactMap { $0.decl.as(VariableDeclSyntax.self) } | ||
|
||
let functionsToMock: [FunctionsMockData] = members | ||
.compactMap { item -> FunctionsMockData? in | ||
guard let casted = item.decl.as(FunctionDeclSyntax.self) else { | ||
return nil | ||
} | ||
|
||
return FunctionsMockData(syntax: casted, accessLevel: accessLevel.tokenSyntax) | ||
} | ||
|
||
return [ | ||
DeclSyntax( | ||
ClassDeclSyntax( | ||
modifiers: .init(itemsBuilder: { | ||
DeclModifierSyntax(name: accessLevel.tokenSyntax) | ||
}), | ||
name: .identifier("\(procotolName)Mock"), | ||
inheritanceClause: .init( | ||
inheritedTypes: .init(itemsBuilder: { | ||
InheritedTypeSyntax( | ||
type: IdentifierTypeSyntax( | ||
name: .identifier(procotolName) | ||
) | ||
) | ||
}) | ||
), | ||
memberBlock: MemberBlockSyntax( | ||
members: try MemberBlockItemListSyntax(itemsBuilder: { | ||
// Classes that has mock data for each function | ||
for funcData in functionsToMock { | ||
ClassMockForFunctionBuilder(funcData: funcData).build() | ||
} | ||
|
||
// Class with all functions mock | ||
FunctionMocksClassBuilder( | ||
functions: functionsToMock, | ||
accessLevel: accessLevel.tokenSyntax | ||
).build() | ||
|
||
// Variable mocks | ||
FunctionMocksClassBuilder( | ||
functions: functionsToMock, | ||
accessLevel: accessLevel.tokenSyntax | ||
).buildVarForTheClass() | ||
|
||
// Implementation of each variable | ||
for variable in variablesToMock { | ||
let varConformance = ProtocolVarsConformanceBuilder( | ||
variable: variable, | ||
accessLevel: accessLevel.tokenSyntax | ||
) | ||
|
||
varConformance.build() | ||
varConformance.buildReturnVar() | ||
} | ||
|
||
// Implementation of each function | ||
for data in functionsToMock { | ||
try ProtocolFunctionsConformanceBuilder( | ||
data: data | ||
).build() | ||
} | ||
}) | ||
) | ||
) | ||
) | ||
] | ||
} | ||
} |
170 changes: 170 additions & 0 deletions
170
...es/SageSwiftKitMacros/MockableMacros/Auxiliars/Builders/ClassMockForFunctionBuilder.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,170 @@ | ||
// | ||
// Copyright © 2024 Sage. | ||
// All Rights Reserved. | ||
|
||
import Foundation | ||
import SwiftSyntax | ||
import SwiftSyntaxMacros | ||
import SwiftSyntaxBuilder | ||
|
||
// Creates the mock for each function with its parameters calls retun value etc... | ||
struct ClassMockForFunctionBuilder { | ||
let funcData: FunctionsMockData | ||
|
||
var parametersName: String { "Parameters" } | ||
var callsName: String { "calls" } | ||
|
||
init(funcData: FunctionsMockData) { | ||
self.funcData = funcData | ||
} | ||
|
||
func build() -> ClassDeclSyntax { | ||
ClassDeclSyntax( | ||
modifiers: .init( | ||
itemsBuilder: { | ||
.init(name: funcData.accessLevel) | ||
} | ||
), | ||
name: funcData.className.tokenSyntax, | ||
memberBlock: .init( | ||
members: .init( | ||
itemsBuilder: { | ||
if let parametersStruct = buildParameters() { | ||
parametersStruct | ||
} | ||
|
||
buildCalls() | ||
buildLastCall() | ||
|
||
buildCalled() | ||
|
||
if let returnValue = funcData.returnValue { | ||
VariableDeclSyntax( | ||
modifiers: .init(itemsBuilder: { | ||
.init(name: funcData.accessLevel) | ||
}), | ||
Keyword.var, | ||
name: "returnValue", | ||
type: TypeAnnotationSyntax( | ||
type: TypeSyntax(stringLiteral: returnValue) | ||
) | ||
) | ||
} | ||
|
||
InitializerDeclSyntax( | ||
signature: .init(parameterClause: .init( | ||
parameters: .init(itemsBuilder: { | ||
|
||
}) | ||
)), | ||
body: .init(statements: .init(itemsBuilder: { | ||
|
||
})) | ||
) | ||
} | ||
) | ||
) | ||
) | ||
} | ||
} | ||
|
||
extension ClassMockForFunctionBuilder { | ||
func buildParameters() -> StructDeclSyntax? { | ||
return StructDeclSyntax( | ||
modifiers: .init(itemsBuilder: { | ||
.init(name: funcData.accessLevel) | ||
}), | ||
name: parametersName.tokenSyntax, | ||
memberBlock: .init(members: .init(itemsBuilder: { | ||
for parameter in funcData.params { | ||
VariableDeclSyntax( | ||
modifiers: .init(itemsBuilder: { | ||
.init(name: funcData.accessLevel) | ||
}), | ||
Keyword.let, | ||
name: PatternSyntax(stringLiteral: parameter.name), | ||
type: TypeAnnotationSyntax( | ||
type: TypeSyntax(stringLiteral: parameter.noEscapingType) | ||
) | ||
) | ||
} | ||
})) | ||
) | ||
} | ||
|
||
func buildCalls() -> VariableDeclSyntax { | ||
return VariableDeclSyntax( | ||
modifiers: .init(itemsBuilder: { | ||
.init(name: funcData.accessLevel) | ||
}), | ||
Keyword.var, | ||
name: .init(stringLiteral: callsName), | ||
type: TypeAnnotationSyntax( | ||
type: TypeSyntax(stringLiteral: "[\(parametersName)]") | ||
), | ||
initializer: InitializerClauseSyntax( | ||
value: ArrayExprSyntax(elements: .init(itemsBuilder: { | ||
|
||
})) | ||
) | ||
) | ||
} | ||
|
||
func buildCalled() -> VariableDeclSyntax { | ||
return VariableDeclSyntax( | ||
modifiers: .init(itemsBuilder: { | ||
.init(name: funcData.accessLevel) | ||
}), | ||
bindingSpecifier: .keyword(.var), | ||
bindings: .init(itemsBuilder: { | ||
PatternBindingSyntax( | ||
pattern: IdentifierPatternSyntax(identifier: "called"), | ||
typeAnnotation: TypeAnnotationSyntax(type: IdentifierTypeSyntax(name: "Bool")), | ||
accessorBlock: AccessorBlockSyntax( | ||
leftBrace: .leftBraceToken(), | ||
accessors: .init(CodeBlockItemListSyntax(itemsBuilder: { | ||
ReturnStmtSyntax( | ||
returnKeyword: .keyword(.return), | ||
expression: DeclReferenceExprSyntax( | ||
baseName: "self.lastCall != nil".tokenSyntax | ||
) | ||
) | ||
})), | ||
rightBrace: .rightBraceToken() | ||
) | ||
) | ||
}) | ||
) | ||
} | ||
|
||
func buildLastCall() -> VariableDeclSyntax { | ||
return VariableDeclSyntax( | ||
modifiers: .init(itemsBuilder: { | ||
.init(name: funcData.accessLevel) | ||
}), | ||
bindingSpecifier: .keyword(.var), | ||
bindings: .init(itemsBuilder: { | ||
PatternBindingSyntax( | ||
pattern: IdentifierPatternSyntax(identifier: "lastCall"), | ||
typeAnnotation: TypeAnnotationSyntax( | ||
type: OptionalTypeSyntax( | ||
wrappedType: IdentifierTypeSyntax(name: parametersName.tokenSyntax) | ||
) | ||
), | ||
accessorBlock: AccessorBlockSyntax( | ||
leftBrace: .leftBraceToken(), | ||
accessors: .init(CodeBlockItemListSyntax(itemsBuilder: { | ||
ReturnStmtSyntax( | ||
returnKeyword: .keyword(.return), | ||
expression: DeclReferenceExprSyntax( | ||
baseName: "self.calls.last".tokenSyntax | ||
) | ||
) | ||
})), | ||
rightBrace: .rightBraceToken() | ||
) | ||
) | ||
}) | ||
) | ||
} | ||
} |
Oops, something went wrong.