Skip to content

Commit 49bc404

Browse files
authored
Merge pull request #139 from unsignedapps/macro-alignment
Simplify/align macro overloads
2 parents a6f1cb3 + fa2abe1 commit 49bc404

25 files changed

+355
-374
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.DS_Store
2-
/.build
2+
.build
33
/build
44
/.swiftpm
55
/Packages

Sources/Vexil/Flag.swift

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,6 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14-
/// Creates a flag with the specified configuration.
15-
///
16-
/// All Flags must be initialised with a default value and a description.
17-
/// The default value is used when none of the sources on the `FlagPole`
18-
/// have a value specified for this flag. The description is used for future
19-
/// developer reference and in Vexlliographer to describe the flag.
20-
///
21-
/// The type that you wrap with `@Flag` must conform to `FlagValue`.
22-
///
23-
/// You can access flag details and observe flag value changes using a peer
24-
/// property prefixed with `$`.
25-
///
26-
/// ```swift
27-
/// @Flag(default: false, description: "My magical flag")
28-
/// var magicFlag: Bool
29-
///
30-
/// // Subscribe to flag updates
31-
/// for try await magic in $magicFlag {
32-
/// // Do magic thing
33-
/// }
34-
///
35-
/// // Also works with Combine
36-
/// $magicFlag
37-
/// .sink { magic in
38-
/// // Do magic thing
39-
/// }
40-
/// ```
41-
///
42-
/// - Parameters:
43-
/// - name: An optional display name to give the flag. Only visible in flag editors like Vexillographer.
44-
/// Default is to calculate one based on the property name.
45-
/// - keyStrategy: An optional strategy to use when calculating the key name. The default is to use the `FlagPole`s strategy.
46-
/// - default: The default value for this `Flag` should no sources have it set.
47-
/// - description: A description of this flag. Used in flag editors like Vexillographer,
48-
/// and also for future developer context.
49-
/// - display: How the flag should be displayed in Vexillographer. Defaults to `.default`,
50-
/// you can set it to `.hidden` to hide the flag.
51-
///
52-
@attached(accessor)
53-
@attached(peer, names: prefixed(`$`))
54-
public macro Flag<Value: FlagValue>(
55-
name: StaticString? = nil,
56-
keyStrategy: VexilConfiguration.FlagKeyStrategy = .default,
57-
default initialValue: Value,
58-
description: StaticString,
59-
display: FlagDisplayOption = .default
60-
) = #externalMacro(module: "VexilMacros", type: "FlagMacro")
61-
6214
/// Creates a flag with the specified configuration.
6315
///
6416
/// All Flags must be initialised via the property and include a description.

Sources/Vexil/Group.swift

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,90 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14+
/// Creates a FlagGroup with the given parameters.
15+
///
16+
/// All FlagGroup's must have at least a `description` which is used for future developer
17+
/// reference and within Vexillographer, our generated flag management UI.
18+
///
19+
/// Attach this to a property within a `@FlagContainer`. The property's type must also
20+
/// be a `FlagContainer`.
21+
///
22+
/// ```swift
23+
/// @FlagContainer
24+
/// struct MyFlags {
25+
/// @FlagGroup("These flags are displayed inside a `NavigationLink` (this is the default)", display: .navigation)
26+
/// var navigation: NavigationFlags
27+
///
28+
/// @FlagGroup("These flags are displayed as a `Section`", display: .section)
29+
/// var section: SectionedFlags
30+
/// }
31+
///
32+
/// @FlagContainer
33+
/// struct NavigationFlags {
34+
/// @Flag("First Flag")
35+
/// var first = false
36+
/// }
37+
///
38+
/// @FlagContainer
39+
/// struct SectionedFlags {
40+
/// @Flag("Second Flag")
41+
/// var second = false
42+
/// }
43+
/// ```
44+
///
45+
/// - Parameters:
46+
/// - description: A description of this flag group. Used in flag editors like Vexillographer, and also for future developer context.
47+
/// - display: How the flag should be displayed in Vexillographer. Defaults to `.navigation` which wraps it in a
48+
/// `NavigationLink`. Other options include `.section` to wrap it in a `Section` and `.hidden`
49+
/// to hide it from Vexillographer entirely.
50+
///
51+
@attached(accessor)
52+
@attached(peer, names: prefixed(`$`))
53+
public macro FlagGroup(
54+
_ description: StaticString,
55+
display: FlagGroupDisplayOption = .navigation
56+
) = #externalMacro(module: "VexilMacros", type: "FlagGroupMacro")
57+
58+
/// Creates a FlagGroup with the given parameters.
59+
///
60+
/// All FlagGroup's must have at least a `description` which is used for future developer
61+
/// reference and within Vexillographer, our generated flag management UI.
62+
///
63+
/// Attach this to a property within a `@FlagContainer`. The property's type must also
64+
/// be a `FlagContainer`.
65+
///
66+
/// ```swift
67+
/// @FlagContainer
68+
/// struct MyFlags {
69+
/// @FlagGroup(name: "Navigation Flags", description: "These flags are displayed inside a `NavigationLink` (this is the default)", display: .navigation)
70+
/// var navigation: NavigationFlags
71+
///
72+
/// @FlagGroup(name: "Section Flags", description: "These flags are displayed as a `Section`", display: .section)
73+
/// var section: SectionedFlags
74+
/// }
75+
///
76+
/// @FlagContainer
77+
/// struct NavigationFlags {
78+
/// @Flag("First Flag")
79+
/// var first = false
80+
/// }
81+
///
82+
/// @FlagContainer
83+
/// struct SectionedFlags {
84+
/// @Flag("Second Flag")
85+
/// var second = false
86+
/// }
87+
/// ```
88+
///
89+
/// - Parameters:
90+
/// - name: An optional display name to give the flag group. Only visible in flag editors like Vexillographer.
91+
/// Default is to calculate one based on the property name.
92+
/// - keyStrategy: An optional strategy to use when calculating the key name. The default is to use the `FlagPole`s strategy.
93+
/// - description: A description of this flag group. Used in flag editors like Vexillographer, and also for future developer context.
94+
/// - display: How the flag should be displayed in Vexillographer. Defaults to `.navigation` which wraps it in a
95+
/// `NavigationLink`. Other options include `.section` to wrap it in a `Section` and `.hidden`
96+
/// to hide it from Vexillographer entirely.
97+
///
1498
@attached(accessor)
1599
@attached(peer, names: prefixed(`$`))
16100
public macro FlagGroup(

Sources/Vexil/Vexil.docc/DefiningFlags.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,14 @@ import Vexil
6969

7070
struct NormalFlags: FlagContainer {
7171

72-
@Flag(default: 10, "This is a demonstration Int flag")
73-
var myIntFlag: Int
72+
@Flag("This is a demonstration Int flag")
73+
var myIntFlag = 10
7474

75-
@Flag(default: 0.5, "This is a demonstration Double flag")
76-
var myDoubleFlag: Double
75+
@Flag("This is a demonstration Double flag")
76+
var myDoubleFlag = 0.5
7777

78-
@Flag(default: "Placeholder", "This is a demonstration String flag")
79-
var myStringFlag: String
78+
@Flag("This is a demonstration String flag")
79+
var myStringFlag = "Placeholder""
8080
8181
}
8282
```
@@ -98,8 +98,8 @@ enum MyTheme: String, FlagValue, CaseIterable {
9898
9999
struct ThemeFlags {
100100
101-
@Flag(default: .blue, "The theme to use for the app")
102-
var currentTheme: MyTheme
101+
@Flag("The theme to use for the app")
102+
var currentTheme = MyTheme.blue
103103
104104
}
105105
```
@@ -117,8 +117,8 @@ struct MyStruct: FlagValue, Codable {
117117
118118
struct TestFlags: FlagContainer {
119119
120-
@Flag(defaultValue: MyStruct(property1: "abc123", property2: 123, property3: "🤯"), description: "...")
121-
var testFlag: MyStruct
120+
@Flag(description: "...")
121+
var testFlag = MyStruct(property1: "abc123", property2: 123, property3: "🤯")
122122
123123
}
124124
```

Sources/Vexil/Vexil.docc/FlagKeys.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ print(flagPole.subgroup.secondSubgroup.$myAwesomeFlag.key)
7373
Sometimes though you want to override how a specific flag calculates its key. Vexil allows you to pass in a ``Flag/CodingKeyStrategy`` when you declare your ``Flag`` to alter how its key is calculated:
7474

7575
```swift
76-
@Flag(codingKeyStrategy: .snakecase, default: false, description: "My Awesome Flag")
77-
var myAwesomeFlag: Bool
76+
@Flag(codingKeyStrategy: .snakecase, description: "My Awesome Flag")
77+
var myAwesomeFlag = false
7878

7979
// Key is "subgroup.second-subgroup.my_awesome_flag"
8080
```
@@ -86,8 +86,8 @@ That would leave `myAwesomeFlag` calculating its key as `"subgroup.second-subgro
8686
You can also go for a manually specified key instead of a calculated one using a ``Flag/CodingKeyStrategy`` of `.customKey("my-key")`:
8787

8888
```swift
89-
@Flag(codingKeyStrategy: .customKey("my-key"), default: false, description: "My Awesome Flag")
90-
var myAwesomeFlag: Bool
89+
@Flag(codingKeyStrategy: .customKey("my-key"), description: "My Awesome Flag")
90+
var myAwesomeFlag = false
9191

9292
// Key is "subgroup.second-subgroup.my-key"
9393
```
@@ -97,8 +97,8 @@ var myAwesomeFlag: Bool
9797
But sometimes your ``FlagValueSource`` doesn't play nice, or the people naming flags in the backend don't provide the same structure that you want your local flags to be in. You can instead set a manual key path. In this case the ``FlagPole`` will ignore the location of the ``Flag`` in the flag structure and will just use the key you specify.
9898

9999
```swift
100-
@Flag(codingKeyStrategy: .customKeyPath("my-key"), default: false, description: "My Awesome Flag")
101-
var myAwesomeFlag: Bool
100+
@Flag(codingKeyStrategy: .customKeyPath("my-key"), description: "My Awesome Flag")
101+
var myAwesomeFlag = false
102102

103103
// Key is "my-key"
104104
```
@@ -110,7 +110,7 @@ While a ``FlagGroup`` doesn't have an explicit key of its own, it does form part
110110
```swift
111111
struct MyFlags: FlagContainer {
112112

113-
@FlagGroup(description: "A subgroup of flags")
113+
@FlagGroup("A subgroup of flags")
114114
var subgroup: Subgroup
115115

116116
}

Sources/Vexil/Vexil.docc/Migration2-3.md

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ struct MyFlags: FlagContainer {
5757
@FlagContainer
5858
struct MyFlags {
5959

60-
@Flag(default: false, description: "Test flag that does something magical")
61-
var testFlag: Bool
60+
@Flag("Test flag that does something magical")
61+
var testFlag = false
6262

63-
@FlagGroup(description: "Some nested flags")
63+
@FlagGroup("Some nested flags")
6464
var nested: NestedFlags
6565

6666
}
@@ -117,12 +117,19 @@ FlagContainer.init(
117117
Under Vexil 3, this is now a macro:
118118

119119
```swift
120+
// Full macro
120121
public macro FlagGroup(
121122
name: StaticString? = nil,
122123
keyStrategy: VexilConfiguration.GroupKeyStrategy = .default,
123124
description: StaticString,
124125
display: VexilDisplayOption = .navigation
125126
)
127+
128+
// Or shorter
129+
public macro FlagGroup(
130+
_ description: StaticString,
131+
display: VexilDisplayOption = .navigation
132+
)
126133
```
127134

128135
As you can see the changes here purely for simplification: `codingKeyStrategy`
@@ -136,14 +143,14 @@ could set your description to `.hidden`; now you pass `.hidden` to display:
136143
var nested: NestedFlags
137144

138145
// Vexil 3
139-
@FlagGroup(description: "Nested flags", display: .hidden)
146+
@FlagGroup("Nested flags", display: .hidden)
140147
var nested: NestedFlags
141148
```
142149

143150
### Flags
144151

145152
Much like Flag Groups, the `@Flag` property wrapper was replaced with the
146-
``Flag(name:keyStrategy:default:description:)`` macro, with simplified parameters:
153+
``Flag(name:keyStrategy:description:display:)`` macro, with simplified parameters:
147154

148155
```swift
149156
// Vexil 2
@@ -156,9 +163,6 @@ var magic = false
156163

157164
// Vexil 3
158165

159-
@Flag(default: false, description: "Flag that enables magic")
160-
var magic: Bool
161-
162166
@Flag("Flag that enables magic")
163167
var magic = false
164168
```
@@ -184,18 +188,9 @@ init(
184188
)
185189
```
186190

187-
Both approaches are available via the `@Flag` macro:
191+
With the `@Flag` macro we now require the property initialiser.
188192

189193
```swift
190-
/// Explicit default parameter
191-
macro Flag<Value: FlagValue>(
192-
name: StaticString? = nil,
193-
keyStrategy: VexilConfiguration.FlagKeyStrategy = .default,
194-
default initialValue: Value,
195-
description: StaticString,
196-
display: FlagDisplayOption = .default
197-
)
198-
199194
/// Sets default via property initialiser
200195
macro Flag(
201196
name: StaticString? = nil,
@@ -211,6 +206,19 @@ macro Flag(_ description: StaticString)
211206
Same as with the `FlagGroup`, the `codingKeyStrategy` parameter has been shortened
212207
to `keyStrategy`, and the ability to hide flags has been moved to the `display` property.
213208

209+
>Note:
210+
>
211+
>The `default:` parameter option was removed as it included a large foot-gun with cryptic
212+
>error message. It was decided to remove this potential issue by aligning with
213+
>[swift-argument-parser](https://github.com/apple/swift-argument-parser)'s approach.
214+
>
215+
>As the macro is type-checked without reference to the attached property's type you get errors like:
216+
>
217+
>```swift
218+
>@Flag(default: .enumCase, description: "") // compiler error: Type of `.enumCase` cannot be inferred.
219+
>var myFlag: MyEnum
220+
>```
221+
214222
## Flag Pole Observation
215223
216224
Under Vexil 2, every time a `FlagValueSource` reported a flag value change, we would

Sources/Vexil/Vexil.docc/Vexil.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,14 @@ let snapshot = flagPole.snapshot()
148148
### Flags
149149

150150
- <doc:DefiningFlags>
151-
- ``Flag(name:keyStrategy:default:description:display:)``
152151
- ``Flag(name:keyStrategy:description:display:)``
153152
- ``Flag(_:)``
154153
- ``FlagValue``
155154

156155
### Flag Groups
157156

158157
- ``FlagGroup(name:keyStrategy:description:display:)``
158+
- ``FlagGroup(_:display:)``
159159
- ``FlagContainer(generateEquatable:)``
160160

161161
### Snapshots

Sources/VexilMacros/FlagContainerMacro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ extension FlagContainerMacro: ExtensionMacro {
170170
])
171171
}
172172
}
173-
ExprSyntax("lhs.\(lastBinding) == rhs.\(lastBinding)")
173+
ExprSyntax("lhs.\(lastBinding.trimmed) == rhs.\(lastBinding.trimmed)")
174174
}
175175
}
176176
.with(\.modifiers, Array(scopes) + [ DeclModifierSyntax(name: .keyword(.static)) ])

Sources/VexilMacros/FlagGroupMacro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public struct FlagGroupMacro {
5656
self.scopes = property.modifiers.scopeSyntax
5757

5858
self.name = arguments[label: "name"]?.expression
59-
self.description = arguments[label: "description"]?.expression
59+
self.description = arguments[label: "description"]?.expression ?? arguments[label: nil]?.expression
6060
self.displayOption = arguments[label: "display"]?.expression
6161
}
6262

0 commit comments

Comments
 (0)