Skip to content

Commit

Permalink
Auto try to use stub on unknown type (#16)
Browse files Browse the repository at this point in the history
* Make it try to use stub by default

* Make all macro by default have static stub and suffixed Stub

* Remove unused error

* Refactor to satisfy swiftlint
  • Loading branch information
hainayanda authored Apr 21, 2024
1 parent e02cec4 commit 82b8cbd
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 9 deletions.
2 changes: 2 additions & 0 deletions Sources/SwiftEnvironment/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ public macro EnvironmentValue() = #externalMacro(
module: "SwiftEnvironmentMacro", type: "EnvironmentValueMacro"
)

@attached(peer, names: suffixed(Stub))
@attached(member, names: arbitrary)
public macro Stubbed(public: Bool = false) = #externalMacro(
module: "SwiftEnvironmentMacro", type: "StubFromTypeGeneratorMacro"
)

@attached(peer, names: suffixed(Stub))
@attached(member, names: arbitrary)
public macro Stubbed(public: Bool = false, _ values: DefaultType...) = #externalMacro(
module: "SwiftEnvironmentMacro", type: "StubFromTypeGeneratorMacro"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Foundation
public enum StubGeneratorMacroError: CustomStringConvertible, Error {
case failedToExtractVariables
case cannotDetermineDefaultValue(String)
case unknownSubType(String)
case unknownArguments(String)
case cannotUseStructForObjectProtocol
case cannotDetermineType(String)
Expand All @@ -21,8 +20,6 @@ public enum StubGeneratorMacroError: CustomStringConvertible, Error {
return "@Stubbed failed to extract one of variables"
case .cannotDetermineDefaultValue(let type):
return "@Stubbed failed to determine default value for \(type)"
case .unknownSubType(let type):
return "@Stubbed failed to determine type of stub: \(type)"
case .unknownArguments(let arg):
return "@Stubbed got unknown argument of \(arg)"
case .cannotUseStructForObjectProtocol:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ extension TypeSyntax {
}
return "(\(partial))"
} else {
throw StubGeneratorMacroError.cannotDetermineDefaultValue(trimmedDescription)
return "\(trimmedDescription)Stub.stub"
}
}
}
10 changes: 9 additions & 1 deletion Sources/SwiftEnvironmentMacro/Model/StubDeclaration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ struct StubDeclaration: CustomStringConvertible {

var description: String {
let declaration = "\(instanceType.declarationString(for: name)) {\n"
let withVariables = variables.reduce(declaration) { partialResult, variable in
let withStaticStub = "\(declaration) static var stub: \(instanceType.protocolName) { \(name)() }\n"
let withVariables = variables.reduce(withStaticStub) { partialResult, variable in
"\(partialResult) \(variable.description)\n"
}
let withInit = "\(withVariables) init() { }\n"
Expand Down Expand Up @@ -45,5 +46,12 @@ extension StubDeclaration {
guard let superClass else { return "" }
return "\(superClass), "
}

var protocolName: String {
switch self {
case .class(let protocolName, _, _), .struct(let protocolName):
return protocolName
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,43 @@ public struct StubFromTypeGeneratorMacro: MemberMacro {
}
}

extension StubFromTypeGeneratorMacro: PeerMacro {
public static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
guard let name = declaration.className ?? declaration.structName else {
return []
}
let isPublic = declaration.isPublic
let modifier = isPublic ? "public ": ""
let typeAlias = "\(modifier)typealias \(name)Stub = \(name)"
return ["\(raw: typeAlias)"]
}
}

// MARK: Private extensions

private extension DeclSyntaxProtocol {
var structName: String? {
self.as(StructDeclSyntax.self)?.name.trimmedDescription
}
var className: String? {
self.as(ClassDeclSyntax.self)?.name.trimmedDescription
}
var isClass: Bool {
self.as(ClassDeclSyntax.self) != nil
}
var isPublic: Bool {
if isClass {
return self.as(ClassDeclSyntax.self)?.modifiers
.map { $0.trimmedDescription }
.contains("public") ?? false
} else {
return self.as(StructDeclSyntax.self)?.modifiers
.map { $0.trimmedDescription }
.contains("public") ?? false
}
}
}

private extension DeclGroupSyntax {
var structName: String? {
self.as(StructDeclSyntax.self)?.name.trimmedDescription
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ protocol Some: AnyObject {
}
final class SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: Int = 0
init() {
}
Expand All @@ -127,6 +130,9 @@ protocol Some {
}
final class SomeStub: Super, Some {
static var stub: Some {
SomeStub()
}
let int: Int = 1
let unknown: Unknown = Unknown()
init() {
Expand All @@ -147,6 +153,9 @@ protocol Some {
}
struct SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: Int = 0
init() {
}
Expand All @@ -168,6 +177,9 @@ protocol Some {
}
struct SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: Int = 1
let unknown: Unknown = Unknown()
init() {
Expand All @@ -188,6 +200,9 @@ protocol Some {
}
final class SomeStub: Super, Some {
static var stub: Some {
SomeStub()
}
let int: Int = 0
init() {
}
Expand All @@ -207,6 +222,9 @@ protocol Some {
}
final class SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: Int = 0
init() {
}
Expand Down Expand Up @@ -248,6 +266,9 @@ protocol Some {
}
struct SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: Int = 0
var double: Double = 0.0
let bool: Bool = false
Expand Down Expand Up @@ -303,6 +324,9 @@ protocol Some {
}
struct SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: Int? = nil
var double: Optional<Double> = nil
let bool: Bool? = nil
Expand Down Expand Up @@ -356,6 +380,9 @@ protocol Some {
}
struct SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: [Int] = []
var double: Array<Double> = []
let bool: [Bool] = []
Expand Down Expand Up @@ -409,6 +436,9 @@ protocol Some {
}
struct SomeStub: Some {
static var stub: Some {
SomeStub()
}
let int: [Int: Unknown] = [:]
var double: Dictionary<Double, Unknown?> = [:]
let bool: [Bool: (Unknown)] = [:]
Expand Down
53 changes: 49 additions & 4 deletions Tests/SwiftEnvironmentTests/StubFromTypeGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ final class StubFromClassAndStructGeneratorMacroTests: XCTestCase {
macros: ["Stubbed": StubFromTypeGeneratorMacro.self]
)
}

func test_givenUnknownTypeStruct_shouldGenerateDefault() {
assertMacroExpansion(
unknownTypeStruct,
expandedSource: unknownTypeStructExpansion,
macros: ["Stubbed": StubFromTypeGeneratorMacro.self]
)
}
}

private let simpleStructWithOptional: String = """
Expand All @@ -108,6 +116,8 @@ struct Some {
Some(optionalClosure: nil, optionalInt: nil, optionalArray: nil, optionalTupple: (nil, nil), realOptional: nil)
}
}
typealias SomeStub = Some
"""

private let simpleClassWithClosure: String = """
Expand Down Expand Up @@ -153,6 +163,8 @@ class Some {
self.argsReturnClosure = argsReturnClosure
}
}
typealias SomeStub = Some
"""
// swiftlint:enable line_length

Expand Down Expand Up @@ -190,6 +202,8 @@ struct Some {
})
}
}
typealias SomeStub = Some
"""

private let simpleClassWithDifferentInit: String = """
Expand Down Expand Up @@ -226,6 +240,8 @@ class Some {
self.double = double
}
}
typealias SomeStub = Some
"""

private let simpleClassWithRandomInit: String = """
Expand Down Expand Up @@ -257,6 +273,8 @@ class Some {
Some(double: 0.0, int: 0)
}
}
typealias SomeStub = Some
"""

private let simpleClassWithInit: String = """
Expand Down Expand Up @@ -288,19 +306,21 @@ class Some {
Some(int: 0, double: 0.0)
}
}
typealias SomeStub = Some
"""

private let simpleClass: String = """
@Stubbed
class Some {
public class Some {
let int: Int
var double: Double
let string: String = ""
}
"""

private let simpleClassExpansion: String = """
class Some {
public class Some {
let int: Int
var double: Double
let string: String = ""
Expand All @@ -314,6 +334,8 @@ class Some {
self.double = double
}
}
public typealias SomeStub = Some
"""

private let simpleStruct: String = """
Expand All @@ -335,23 +357,46 @@ struct Some {
Some(int: 0, double: 0.0)
}
}
typealias SomeStub = Some
"""

private let typeAliasStruct: String = """
@Stubbed
struct Some {
public struct Some {
typealias MyAlias = Int
let int: MyAlias
}
"""

private let typeAliasStructExpansion: String = """
struct Some {
public struct Some {
typealias MyAlias = Int
let int: MyAlias
static var stub: Some {
Some(int: 0)
}
}
public typealias SomeStub = Some
"""

private let unknownTypeStruct: String = """
@Stubbed
public struct Some {
let unknown: Unknown
}
"""

private let unknownTypeStructExpansion: String = """
public struct Some {
let unknown: Unknown
static var stub: Some {
Some(unknown: UnknownStub.stub)
}
}
public typealias SomeStub = Some
"""

0 comments on commit 82b8cbd

Please sign in to comment.