Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
muukii committed May 7, 2024
1 parent 4026be4 commit 0de217b
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 19 deletions.
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ let package = Package(
),
.testTarget(
name: "VergeTests",
dependencies: ["Verge", "ViewInspector"]
dependencies: ["Verge", "ViewInspector"],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]
),
.testTarget(
name: "VergeTinyTests",
Expand Down
138 changes: 123 additions & 15 deletions Sources/Verge/Store/StoreDriverType+Accumulator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,31 @@ extension StoreDriverType {

public func accumulate<T>(
queue: MainActorTargetQueue = .mainIsolated(),
@AccumulationSinkComponentBuilder<Scope> _ buildSubscription: @escaping @MainActor (consuming AccumulationBuilder<Scope>) -> AccumulationSinkGroup<Scope, T>
@AccumulationSinkComponentBuilder<Scope> _ buildSubscription: @escaping @MainActor (consuming AccumulationBuilder<Scope>) -> _AccumulationSinkGroup<Scope, T>
) -> StoreStateSubscription {

var previous: AccumulationSinkGroup<Scope, T>?
var previousBox: ReferenceEdge<_AccumulationSinkGroup<Scope, T>?> = .init(wrappedValue: nil)

return sinkState(dropsFirst: false, queue: queue) { state in

let builder = AccumulationBuilder<Scope>()
let builder = AccumulationBuilder<Scope>(previousLoader: {
previousBox.wrappedValue
})

var group = buildSubscription(consume builder)

// sets the latest value
group = group.receive(source: state.primitiveBox)

// sets the previous value
if let previous {
if let previous = previousBox.wrappedValue {
group = group.receive(other: previous)
}

// runs sink
group = group.consume()

previous = group
previousBox.wrappedValue = group

}

Expand All @@ -42,6 +44,16 @@ public protocol AccumulationSink<Source> {

public struct AccumulationBuilder<Source>: ~Copyable {

public var previous: (any AccumulationSink)? {
previousLoader()
}

private let previousLoader: () -> (any AccumulationSink)?

init(previousLoader: @escaping () -> (any AccumulationSink)?) {
self.previousLoader = previousLoader
}

public func ifChanged<U: Equatable>(_ selector: @escaping (borrowing Source) -> U) -> AccumulationSinkIfChanged<Source, U> {
.init(
selector: selector
Expand Down Expand Up @@ -104,7 +116,7 @@ public struct AccumulationSinkIfChanged<Source, Target: Equatable>: Accumulation
}
}

public struct AccumulationSinkGroup<Source, Component>: AccumulationSink {
public struct _AccumulationSinkGroup<Source, Component>: AccumulationSink {

private var component: Component
private var _receiveSource: (ReadonlyBox<Source>, Component) -> Component
Expand Down Expand Up @@ -135,7 +147,7 @@ public struct AccumulationSinkGroup<Source, Component>: AccumulationSink {
return self
}

public consuming func receive(other: AccumulationSinkGroup<Source, Component>) -> Self {
public consuming func receive(other: _AccumulationSinkGroup<Source, Component>) -> Self {
component = _receiveOther(other.component, component)
return self
}
Expand All @@ -147,21 +159,117 @@ public struct AccumulationSinkGroup<Source, Component>: AccumulationSink {

}

@resultBuilder
public struct AccumulationSinkComponentBuilder<Source> {
public struct _AccumulationSinkCondition<Source, TrueComponent: AccumulationSink, FalseComponent: AccumulationSink>: AccumulationSink where TrueComponent.Source == Source, FalseComponent.Source == Source {

private var trueComponent: TrueComponent?
private var falseComponent: FalseComponent?

init(
trueComponent: TrueComponent?,
falseComponent: FalseComponent?
) {
self.trueComponent = trueComponent
self.falseComponent = falseComponent
}

public consuming func receive(source: ReadonlyBox<Source>) -> Self {
if let trueComponent = trueComponent {
self.trueComponent = trueComponent.receive(source: source)
} else if let falseComponent = falseComponent {
self.falseComponent = falseComponent.receive(source: source)
}
return self
}

public consuming func receive(other: _AccumulationSinkCondition<Source, TrueComponent, FalseComponent>) -> Self {
if let trueComponent = trueComponent, let otherTrueComponent = other.trueComponent {
self.trueComponent = trueComponent.receive(other: otherTrueComponent)
} else if let falseComponent = falseComponent, let otherFalseComponent = other.falseComponent {
self.falseComponent = falseComponent.receive(other: otherFalseComponent)
}
return self
}

public consuming func consume() -> Self {
if let trueComponent = trueComponent {
self.trueComponent = trueComponent.consume()
} else if let falseComponent = falseComponent {
self.falseComponent = falseComponent.consume()
}
return self
}

}

struct _AccumulationSinkOptional<Source, Component: AccumulationSink>: AccumulationSink where Component.Source == Source {

private var component: Component?

init(
component: Component?
) {
self.component = component
}

consuming func receive(source: ReadonlyBox<Source>) -> Self {
if let component = component {
self.component = component.receive(source: source)
}
return self
}

consuming func receive(other: _AccumulationSinkOptional<Source, Component>) -> Self {
if let component = component, let otherComponent = other.component {
self.component = component.receive(other: otherComponent)
}
return self
}

public static func buildBlock() -> AccumulationSinkGroup<Source, Void> {
return .init()
consuming func consume() -> Self {
if let component = component {
self.component = component.consume()
}
return self
}

public static func buildExpression<S: AccumulationSink>(_ expression: S) -> some AccumulationSink<Source> where S.Source == Source {
}

@resultBuilder
public struct AccumulationSinkComponentBuilder<Source> {

public static func buildExpression<S: AccumulationSink>(_ expression: S) -> S where S.Source == Source {
expression
}


public static func buildBlock() -> some AccumulationSink {
return _AccumulationSinkGroup<Source, Void>()
}

public static func buildEither<TrueComponent: AccumulationSink, FalseComponent: AccumulationSink>(first component: TrueComponent) -> _AccumulationSinkCondition<Source, TrueComponent, FalseComponent> where TrueComponent.Source == Source, FalseComponent.Source == Source {

return _AccumulationSinkCondition<Source, TrueComponent, FalseComponent>(
trueComponent: component,
falseComponent: nil
)

}

public static func buildEither<TrueComponent: AccumulationSink, FalseComponent: AccumulationSink>(second component: FalseComponent) -> _AccumulationSinkCondition<Source, TrueComponent, FalseComponent> where TrueComponent.Source == Source, FalseComponent.Source == Source {

return _AccumulationSinkCondition<Source, TrueComponent, FalseComponent>(
trueComponent: nil,
falseComponent: component
)
}

public static func buildOptional<Component>(_ component: Component?) -> some AccumulationSink where Component : AccumulationSink {
return _AccumulationSinkOptional.init(component: component)
}

// FIXME: add `where repeat (each S).Source == Source`
public static func buildBlock<each S: AccumulationSink>(_ sinks: repeat each S) -> AccumulationSinkGroup<Source, (repeat each S)> {
public static func buildBlock<each S: AccumulationSink>(_ sinks: repeat each S) -> _AccumulationSinkGroup<Source, (repeat each S)> {

return AccumulationSinkGroup<Source, (repeat each S)>(
return _AccumulationSinkGroup<Source, (repeat each S)>(
component: (repeat each sinks),
receiveSource: { source, component in
// Waiting https://www.swift.org/blog/pack-iteration/
Expand Down
25 changes: 25 additions & 0 deletions Tests/VergeTests/AccumulationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,31 @@ final class AccumulationTests: XCTestCase {
expForName.fulfill()
}

// checks for result builders
if let _ = self {
$0.ifChanged(\.name).do { value in
runMain()
}
}

// checks for result builders
if true {
$0.ifChanged(\.name).do { value in
runMain()
}
} else {
$0.ifChanged(\.name).do { value in
runMain()
}
}

// checks for result builders
if true {
$0.ifChanged(\.name).do { value in
runMain()
}
}

}

store.commit {
Expand Down
9 changes: 6 additions & 3 deletions Tests/VergeTests/ConcurrencyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,17 @@ final class ConcurrencyTests: XCTestCase {
counter.fulfill()
}

var dispatched = Array<Int>()
let dispatched = VergeConcurrency.UnfairLockAtomic<Array<Int>>([])

DispatchQueue.global().async {
DispatchQueue.concurrentPerform(iterations: 100) { i in

Task {
await store.backgroundCommit {
$0.count = i
dispatched.append(i)
dispatched.modify {
$0.append(i)
}
}
}
}
Expand All @@ -62,7 +64,8 @@ final class ConcurrencyTests: XCTestCase {

wait(for: [exp, counter], timeout: 10)
// print(dispatched, results)
XCTAssertEqual([0] + dispatched, results.value, "\(([0] + dispatched).difference(from: results.value))")
let _dispatched = dispatched.value
XCTAssertEqual([0] + _dispatched, results.value, "\(([0] + _dispatched).difference(from: results.value))")
withExtendedLifetime(sub) {}
}

Expand Down

0 comments on commit 0de217b

Please sign in to comment.