Sources/MockoloTestSupportMacros/Fixture.swift (46 lines of code) (raw):
import SwiftBasicFormat
import SwiftSyntax
import SwiftSyntaxMacros
struct Fixture: MemberMacro {
static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let baseItems = declaration.memberBlock.members.filter { (item: MemberBlockItemSyntax) in
if let decl = item.decl.asProtocol(WithAttributesSyntax.self) {
let isFixtureAnnotated = decl.attributes.contains { (attr: AttributeListSyntax.Element) in
return attr.trimmedDescription == "@Fixture"
}
return !isFixtureAnnotated
}
return true
}
let indent = BasicFormat.inferIndentation(of: declaration) ?? .spaces(4)
let sourceContent = baseItems.trimmedDescription(matching: \.isNewline)
let varDecl = VariableDeclSyntax(
modifiers: [.init(name: .keyword(.static))],
.let,
name: "_source",
initializer: .init(
value: StringLiteralExprSyntax(
multilineContent: sourceContent,
endIndent: Trivia(pieces: node.leadingTrivia.filter(\.isSpaceOrTab)) + indent
)
)
)
return [DeclSyntax(varDecl)]
}
}
extension StringLiteralExprSyntax {
fileprivate init(multilineContent: String, endIndent: Trivia) {
self = StringLiteralExprSyntax(
openingPounds: .rawStringPoundDelimiter("##"),
openingQuote: .multilineStringQuoteToken(),
segments: [.stringSegment(.init(content: .stringSegment(multilineContent)))],
closingQuote: .multilineStringQuoteToken(leadingTrivia: endIndent),
closingPounds: .rawStringPoundDelimiter("##")
)
}
}