Skip to content

Commit e5cf9fa

Browse files
committed
feat: Add ifLetScope operators
1 parent fad7ec5 commit e5cf9fa

File tree

6 files changed

+114
-9
lines changed

6 files changed

+114
-9
lines changed

Sources/FunctionalBuilder/Builder.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,21 @@ extension Builder {
173173
)
174174
}
175175

176+
public func ifLetScope<Wrapped>(
177+
_ builder: @escaping (Builder<Wrapped>) -> Builder<Wrapped>
178+
) -> Builder where Value == Wrapped? {
179+
Builder(
180+
_block.builder._initialValue,
181+
_block.builder._configurator.appendingConfiguration { base in
182+
guard let value = _block.keyPath.extract(from: base) else { return base }
183+
return _block.keyPath.embed(
184+
builder(.init(value)).build(),
185+
in: base
186+
)
187+
}
188+
)
189+
}
190+
176191
public func callAsFunction(
177192
if condition: Bool,
178193
then thenValue: @escaping @autoclosure () -> Value,
@@ -272,6 +287,21 @@ extension Builder {
272287
)
273288
}
274289

290+
public func ifLetScope<Wrapped>(
291+
_ builder: @escaping (Builder<Wrapped>) -> Builder<Wrapped>
292+
) -> Builder where Wrapped: AnyObject, Value == Wrapped? {
293+
Builder(
294+
self.builder._initialValue,
295+
self.builder._configurator.appendingConfiguration { base in
296+
guard let value = keyPath.extract(from: base) else { return base }
297+
return keyPath.embed(
298+
builder(.init(value)).build(),
299+
in: base
300+
)
301+
}
302+
)
303+
}
304+
275305
public subscript<LocalValue>(
276306
dynamicMember keyPath: WritableKeyPath<Value, LocalValue>
277307
) -> CallableBlock<LocalValue> where Value: AnyObject {

Sources/FunctionalClosures/Handler.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ public class Handler<Input> {
2727
https://github.com/MakeupStudio/swift-declarative-configuration/issues/1
2828
"""
2929
)
30-
public mutating func callAsFunction(_ behaviour: Behaviour, perform action: ((Input) -> Void)?)
31-
{
30+
public mutating func callAsFunction(_ behaviour: Behaviour, perform action: ((Input) -> Void)?) {
3231
switch behaviour {
3332
case .resetting:
3433
self.action = action
@@ -107,8 +106,7 @@ public class Handler2<T0, T1> {
107106
https://github.com/MakeupStudio/swift-declarative-configuration/issues/1
108107
"""
109108
)
110-
public mutating func callAsFunction(_ behaviour: Behaviour, perform action: ((T0, T1) -> Void)?)
111-
{
109+
public mutating func callAsFunction(_ behaviour: Behaviour, perform action: ((T0, T1) -> Void)?) {
112110
switch behaviour {
113111
case .resetting:
114112
self.action = action

Sources/FunctionalConfigurator/Configurator.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,18 @@ extension Configurator {
164164
}
165165
}
166166

167+
public func ifLetScope<Wrapped>(
168+
_ configuration: @escaping (Configurator<Wrapped>) -> Configurator<Wrapped>
169+
) -> Configurator where Value == Wrapped? {
170+
_block.configurator.appendingConfiguration { base in
171+
guard let value = _block.keyPath.extract(from: base) else { return base }
172+
return _block.keyPath.embed(
173+
_modification(of: value, with: configuration),
174+
in: base
175+
)
176+
}
177+
}
178+
167179
public subscript<LocalValue>(
168180
dynamicMember keyPath: WritableKeyPath<Value, LocalValue>
169181
) -> CallableBlock<LocalValue> {
@@ -211,6 +223,18 @@ extension Configurator {
211223
}
212224
}
213225

226+
public func ifLetScope<Wrapped>(
227+
_ configuration: @escaping (Configurator<Wrapped>) -> Configurator<Wrapped>
228+
) -> Configurator where Wrapped: AnyObject, Value == Wrapped? {
229+
configurator.appendingConfiguration { base in
230+
guard let value = keyPath.extract(from: base) else { return base }
231+
return keyPath.embed(
232+
_modification(of: value, with: configuration),
233+
in: base
234+
)
235+
}
236+
}
237+
214238
public subscript<LocalValue>(
215239
dynamicMember keyPath: WritableKeyPath<Value, LocalValue>
216240
) -> CallableBlock<LocalValue> where Value: AnyObject {

Tests/DeclarativeConfigurationTests/BuilderTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,19 @@ final class BuilderTests: XCTestCase {
6161
func testScope() {
6262
struct Container: BuilderProvider {
6363
class Content {
64+
class InnerClass {
65+
var value: Int = 0
66+
}
67+
68+
struct InnerStruct {
69+
var value: Int = 0
70+
}
71+
6472
var a: Int = 0
6573
var b: Int = 0
6674
var c: Int = 0
75+
let innerClass: InnerClass? = nil
76+
var innerStruct: InnerStruct? = nil
6777

6878
init() {}
6979
}
@@ -75,22 +85,34 @@ final class BuilderTests: XCTestCase {
7585
.content.a(1)
7686
.content.b(2)
7787
.content.c(3)
88+
.content.innerClass.value(1)
89+
.content.innerStruct.value(1)
7890
.build()
91+
7992
let initial = Container()
8093
let actual = Container().builder
8194
.content.scope {
8295
$0
8396
.a(1)
8497
.b(2)
8598
.c(3)
99+
.innerClass
100+
.ifLetScope {
101+
$0
102+
.value(1)
103+
}
86104
}.build()
87105

88106
XCTAssertNotEqual(actual.content.a, initial.content.a)
89107
XCTAssertNotEqual(actual.content.b, initial.content.b)
90108
XCTAssertNotEqual(actual.content.c, initial.content.c)
109+
XCTAssertEqual(actual.content.innerClass?.value, initial.content.innerClass?.value)
110+
XCTAssertEqual(actual.content.innerStruct?.value, initial.content.innerStruct?.value)
91111

92112
XCTAssertEqual(actual.content.a, expected.content.a)
93113
XCTAssertEqual(actual.content.b, expected.content.b)
94114
XCTAssertEqual(actual.content.c, expected.content.c)
115+
XCTAssertEqual(actual.content.innerClass?.value, expected.content.innerClass?.value)
116+
XCTAssertEqual(actual.content.innerStruct?.value, expected.content.innerStruct?.value)
95117
}
96118
}

Tests/DeclarativeConfigurationTests/ConfiguratorTests.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,19 @@ final class ConfiguratorTests: XCTestCase {
129129
func testScope() {
130130
struct Container: ConfigInitializable {
131131
class Content {
132+
class InnerClass {
133+
var value: Int = 0
134+
}
135+
136+
struct InnerStruct {
137+
var value: Int = 0
138+
}
139+
132140
var a: Int = 0
133141
var b: Int = 0
134142
var c: Int = 0
143+
let innerClass: InnerClass? = nil
144+
var innerStruct: InnerStruct? = nil
135145

136146
init() {}
137147
}
@@ -144,20 +154,40 @@ final class ConfiguratorTests: XCTestCase {
144154
.content.a(1)
145155
.content.b(2)
146156
.content.c(3)
157+
.content.innerClass.value(1)
158+
.content.innerStruct.value(1)
147159
}
148-
160+
let initial = Container()
149161
let actual = Container {
150162
$0
151163
.content.scope {
152164
$0
153165
.a(1)
154166
.b(2)
155167
.c(3)
168+
.innerClass
169+
.ifLetScope {
170+
$0
171+
.value(1)
172+
}
173+
.innerStruct
174+
.ifLetScope {
175+
$0
176+
.value(1)
177+
}
156178
}
157179
}
158180

181+
XCTAssertNotEqual(actual.content.a, initial.content.a)
182+
XCTAssertNotEqual(actual.content.b, initial.content.b)
183+
XCTAssertNotEqual(actual.content.c, initial.content.c)
184+
XCTAssertEqual(actual.content.innerClass?.value, initial.content.innerClass?.value)
185+
XCTAssertEqual(actual.content.innerStruct?.value, initial.content.innerStruct?.value)
186+
159187
XCTAssertEqual(actual.content.a, expected.content.a)
160188
XCTAssertEqual(actual.content.b, expected.content.b)
161189
XCTAssertEqual(actual.content.c, expected.content.c)
190+
XCTAssertEqual(actual.content.innerClass?.value, expected.content.innerClass?.value)
191+
XCTAssertEqual(actual.content.innerStruct?.value, expected.content.innerStruct?.value)
162192
}
163193
}

Tests/DeclarativeConfigurationTests/FunctionalClosuresTests.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ final class FunctionalClosuresTests: XCTestCase {
7575
let storage = Storage()
7676

7777
let object =
78-
Object { $0
79-
// Handle only result
80-
.$handleSum(assignThird(to: storage, \.result))
81-
}
78+
Object {
79+
$0
80+
// Handle only result
81+
.$handleSum(assignThird(to: storage, \.result))
82+
}
8283

8384
let a = 10
8485
let b = 20

0 commit comments

Comments
 (0)