forked from woocommerce/woocommerce-ios
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Fakes.swifttemplate
99 lines (84 loc) · 3.65 KB
/
Fakes.swifttemplate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<%
// --------------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------------
/// A representation of the struct/class/enum to generate. This defines the
/// properties that the template will need to generate the fake method.
///
/// We create our own data structure to clarify what we need in the template code.
/// This also makes the template simpler to read because the complexity are all encapsulated
/// by this struct.
///
struct FakeableSpec {
/// A representation of how the spec will be built.
/// Either by a value(eg: Enums) or by an initializer(eg: Struct, Classes)
///
enum Content {
case value(String)
case initializer([Property])
}
/// A representation of a property for the fake() method.
///
struct Property {
/// The name of the property
let name: String
/// If this is not the last, this will be a literal comma (",")
let commaOrNothing: String
}
/// The name of the struct/class/enum that conforms to GeneratedFakeable.
let name: String
/// The access level "public", "private", etc with a space at the end. This is just an empty
/// string if the true accessLevel is "internal".
let accessLevelWithSpacePostfix: String
/// The content that will be used as the return text of the fake() method.
let content: Content
}
// The matching types that we're going to generate code for.
let matchingTypes = types.based["GeneratedFakeable"].filter {
$0.kind == "struct" || $0.kind == "class" || $0.kind == "enum"
}
// The collection of FakeableSpecs that the template below will use.
let specsToGenerate: [FakeableSpec] = matchingTypes.map { type in
let accessLevel = type.accessLevel == "internal" ? "" : "\(type.accessLevel) "
/// If we are generating for an `Enum`, return with `.value` for content
if let enumType = type as? Enum, let firstEnumCase = enumType.cases.first {
return FakeableSpec(name: enumType.name, accessLevelWithSpacePostfix: accessLevel, content: .value(firstEnumCase.name))
}
/// Make sure we have at least one non throwable-initializer to work with.
guard let initializer = type.initializers.first(where: { !$0.throws }) else {
fatalError("Couldn't find initializer for type: \(type.name)")
}
// Convert initializer's parameters to FakeableSpec.Property instances that the template will be able to use.
let propSpecs: [FakeableSpec.Property] = initializer.parameters.map { parameter in
FakeableSpec.Property(
name: parameter.argumentLabel ?? "" , // TODO: Add support to un-named parameters
commaOrNothing: parameter == initializer.parameters.last ? "" : ","
)
}
return FakeableSpec(name: type.name, accessLevelWithSpacePostfix: accessLevel, content: .initializer(propSpecs))
}
-%>
<%#
// --------------------------------------------------------------------------------
// Template
// --------------------------------------------------------------------------------
-%>
import Yosemite
import Networking
<% for spec in specsToGenerate { -%>
extension <%= spec.name -%> {
/// Returns a "ready to use" type filled with fake values.
///
<%= spec.accessLevelWithSpacePostfix -%> <%_ _%> static func fake() -> <%= spec.name -%> {
<% if case .initializer(let properties) = spec.content { -%>
.init(
<% for property in properties { -%>
<%= property.name -%>: .fake() <%_ _%> <%= property.commaOrNothing %>
<% } -%>
)
<% } else if case .value(let caseName) = spec.content { -%>
.<%= caseName %>
<% } -%>
}
}
<% } -%>