-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMacros.swift
194 lines (173 loc) · 5.75 KB
/
Macros.swift
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxMacros
public struct PositiveMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let number = try node.getNumber()
guard number.isPositive else {
throw SafeTypesMacrosError.notPositive
}
return "Positive(\(raw: number)).unsafelyUnwrapped"
}
}
public struct NegativeMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let number = try node.getNumber()
guard number.isNegative else {
throw SafeTypesMacrosError.notNegative
}
return "Negative(\(raw: number)).unsafelyUnwrapped"
}
}
public struct NonPositiveMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let number = try node.getNumber()
guard !number.isPositive else {
throw SafeTypesMacrosError.positive
}
return "NonPositive(\(raw: number)).unsafelyUnwrapped"
}
}
public struct NonNegativeMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let number = try node.getNumber()
guard !number.isNegative else {
throw SafeTypesMacrosError.negative
}
return "NonNegative(\(raw: number)).unsafelyUnwrapped"
}
}
public struct NonZeroMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let number = try node.getNumber()
guard !number.isZero else {
throw SafeTypesMacrosError.zero
}
return "NonZero(\(raw: number)).unsafelyUnwrapped"
}
}
public struct NonEmptyStringMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let string = try node.getArgument()
guard ExprSyntax("\(raw: string)").description.count > 2 else {
throw SafeTypesMacrosError.empty
}
return "NonEmptyString(\(raw: string)).unsafelyUnwrapped"
}
}
public struct ZeroToOneMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let number = try node.getNumber()
guard number.isLessThanOrEqualToOne else {
throw SafeTypesMacrosError.greaterThanOne
}
guard number.isGreaterThanOrEqualToZero else {
throw SafeTypesMacrosError.lessThanZero
}
return "ZeroToOne(\(raw: number)).unsafelyUnwrapped"
}
}
public struct MinusOneToOneMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
let number = try node.getNumber()
guard number.isLessThanOrEqualToOne else {
throw SafeTypesMacrosError.greaterThanOne
}
guard number.isGreaterThanOrEqualToMinusOne else {
throw SafeTypesMacrosError.lessThanMinusOne
}
return "MinusOneToOne(\(raw: number)).unsafelyUnwrapped"
}
}
typealias Number = Numeric & Comparable
extension FreestandingMacroExpansionSyntax {
func getArgument() throws -> String {
guard let argument = argumentList.first?.expression.description else {
throw SafeTypesMacrosError.missingArguments
}
return argument
}
func getNumber() throws -> any Number {
let argument = try getArgument()
if let integerNumber = Int(argument) {
return integerNumber
} else if let doubleNumber = Double(argument) {
return doubleNumber
} else {
throw SafeTypesMacrosError.notNumberLiteral
}
}
}
@main
struct SafeTypesMacrosPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
PositiveMacro.self,
NegativeMacro.self,
NonPositiveMacro.self,
NonNegativeMacro.self,
NonZeroMacro.self,
NonEmptyStringMacro.self,
ZeroToOneMacro.self,
MinusOneToOneMacro.self
]
}
enum SafeTypesMacrosError: String, Error, CustomStringConvertible {
case missingArguments = "Missing macro argument(s)"
case notNumberLiteral = "Should be number literal"
case notPositive = "Should be positive"
case notNegative = "Should be negative"
case positive = "Should not be positive"
case negative = "Should not be negative"
case zero = "Should not be zero"
case empty = "Should not be empty"
case lessThanZero = "Should be greater than or equal to zero"
case greaterThanOne = "Should be less than or equal to one"
case lessThanMinusOne = "Should be greater than or equal to minus one"
var description: String {
rawValue
}
}
extension Numeric where Self: Comparable {
var isPositive: Bool {
self > .zero
}
var isNegative: Bool {
self < .zero
}
var isZero: Bool {
self == .zero
}
var isLessThanOrEqualToOne: Bool {
self <= 1
}
var isGreaterThanOrEqualToZero: Bool {
self >= 0
}
var isGreaterThanOrEqualToMinusOne: Bool {
self >= -1
}
}