Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions Sources/OneWay/AnyEffect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,22 @@ extension AnyEffect {
).eraseToAnyEffect()
}

/// An effect that concatenates a list of effects together into a single effect, which runs the
/// effects one after the other.
///
/// - Parameters:
/// - priority: The priority of the task.
/// Pass `nil` to use the priority from `Task.currentPriority`.
/// - build: A builder that makes effects.
/// - Returns: A new effect.
@inlinable
public static func concat(
priority: TaskPriority? = nil,
@EffectsBuilder _ effects: () -> [AnyEffect<Element>]
@EffectsBuilder<Element> _ build: () -> [AnyEffect<Element>]
) -> AnyEffect<Element> {
Effects.Concat(
priority: priority,
effects()
build()
).eraseToAnyEffect()
}

Expand All @@ -220,14 +228,22 @@ extension AnyEffect {
).eraseToAnyEffect()
}

/// An effect that merges a list of effects together into a single effect, which runs the
/// effects at the same time.
///
/// - Parameters:
/// - priority: The priority of the task.
/// Pass `nil` to use the priority from `Task.currentPriority`.
/// - build: A builder that makes effects.
/// - Returns: A new effect.
@inlinable
public static func merge(
priority: TaskPriority? = nil,
@EffectsBuilder _ effects: () -> [AnyEffect<Element>]
@EffectsBuilder<Element> _ build: () -> [AnyEffect<Element>]
) -> AnyEffect<Element> {
Effects.Merge(
priority: priority,
effects()
build()
).eraseToAnyEffect()
}
}
15 changes: 0 additions & 15 deletions Sources/OneWay/EffectBuilder.swift

This file was deleted.

52 changes: 52 additions & 0 deletions Sources/OneWay/EffectsBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// OneWay
// The MIT License (MIT)
//
// Copyright (c) 2022-2023 SeungYeop Yeom ( https://github.com/DevYeom ).
//

import Foundation

@resultBuilder
public enum EffectsBuilder<T: Sendable> {
public static func buildArray(_ components: [[AnyEffect<T>]]) -> [AnyEffect<T>] {
components.flatMap { $0 }
}

public static func buildBlock() -> [AnyEffect<T>] {
[]
}

public static func buildBlock(_ effects: AnyEffect<T>...) -> [AnyEffect<T>] {
effects
}

public static func buildBlock(_ components: [AnyEffect<T>]...) -> [AnyEffect<T>] {
components.flatMap { $0 }
}

public static func buildEither(first component: [AnyEffect<T>]) -> [AnyEffect<T>] {
component
}

public static func buildEither(second component: [AnyEffect<T>]) -> [AnyEffect<T>] {
component
}

public static func buildExpression(_ expression: AnyEffect<T>) -> [AnyEffect<T>] {
[expression]
}

public static func buildFinalResult(_ component: [AnyEffect<T>]) -> [AnyEffect<T>] {
component
}

public static func buildLimitedAvailability(_ component: [AnyEffect<T>]) -> [AnyEffect<T>] {
component
}

public static func buildOptional(_ component: [AnyEffect<T>]?) -> [AnyEffect<T>] {
guard let component = component else { return [] }
return component
}
}
263 changes: 263 additions & 0 deletions Tests/OneWayTests/EffectsBuilderTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
//
// OneWay
// The MIT License (MIT)
//
// Copyright (c) 2022-2023 SeungYeop Yeom ( https://github.com/DevYeom ).
//

import OneWay
import XCTest

final class EffectsBuilderTests: XCTestCase {
func test_array() async {
do {
let effect = AnyEffect<Int>.concat {
let effects = [
AnyEffect.just(1),
AnyEffect.just(2),
AnyEffect.just(3),
]
for effect in effects {
effect
}
}

var result: [Int] = []
for await value in effect.values {
result.append(value)
}

XCTAssertEqual(result, [1, 2, 3])
}

do {
let effect = AnyEffect<Int>.merge {
let effects = [
AnyEffect.just(1),
AnyEffect.just(2),
AnyEffect.just(3),
]
for effect in effects {
effect
}
}

var result: Set<Int> = []
for await value in effect.values {
result.insert(value)
}

XCTAssertEqual(result, [1, 2, 3])
}
}

func test_emptyBlock() async {
do {
let effect = AnyEffect<Int>.concat { }

var result: [Int] = []
for await value in effect.values {
result.append(value)
}

XCTAssertEqual(result, [])
}

do {
let effect = AnyEffect<Int>.merge { }

var result: Set<Int> = []
for await value in effect.values {
result.insert(value)
}

XCTAssertEqual(result, [])
}
}

func test_block() async {
do {
let effect = AnyEffect<Int>.concat {
AnyEffect.just(1)
AnyEffect.just(2)
AnyEffect.just(3)
}

var result: [Int] = []
for await value in effect.values {
result.append(value)
}

XCTAssertEqual(result, [1, 2, 3])
}

do {
let effect = AnyEffect<Int>.merge {
AnyEffect.just(1)
AnyEffect.just(2)
AnyEffect.just(3)
}

var result: Set<Int> = []
for await value in effect.values {
result.insert(value)
}

XCTAssertEqual(result, [1, 2, 3])
}
}

func test_conditionalBlock() async {
enum Order {
case first
case second
}
let trueCondition = true
let falseCondition = false
let order = Order.second

do {
let effect = AnyEffect<Int>.concat {
AnyEffect.just(1)

if trueCondition {
AnyEffect.just(2)
}
if falseCondition {
AnyEffect.just(3)
} else {
AnyEffect.just(4)
}

switch order {
case .first:
AnyEffect.just(5)
case .second:
AnyEffect.just(6)
}
}

var result: [Int] = []
for await value in effect.values {
result.append(value)
}

XCTAssertEqual(result, [1, 2, 4, 6])
}

do {
let effect = AnyEffect<Int>.merge {
AnyEffect.just(1)

if trueCondition {
AnyEffect.just(2)
}
if falseCondition {
AnyEffect.just(3)
} else {
AnyEffect.just(4)
}

switch order {
case .first:
AnyEffect.just(5)
case .second:
AnyEffect.just(6)
}
}

var result: Set<Int> = []
for await value in effect.values {
result.insert(value)
}

XCTAssertEqual(result, [1, 2, 4, 6])
}
}

func test_optionalBlock() async {
let someValue: AnyEffect<Int>? = .just(1)
let someValue2: AnyEffect<Int>? = .just(2)
let noneValue: AnyEffect<Int>? = nil

do {
let effect = AnyEffect<Int>.concat {
if let someValue {
someValue
}
if case let .some(value) = someValue2 {
value
}
if let noneValue {
noneValue
}
}

var result: [Int] = []
for await value in effect.values {
result.append(value)
}

XCTAssertEqual(result, [1, 2])
}

do {
let effect = AnyEffect<Int>.merge {
if let someValue {
someValue
}
if case let .some(value) = someValue2 {
value
}
if let noneValue {
noneValue
}
}

var result: Set<Int> = []
for await value in effect.values {
result.insert(value)
}

XCTAssertEqual(result, [1, 2])
}
}

func test_limitedAvailabilityBlock() async {
do {
let effect = AnyEffect<Int>.concat {
AnyEffect.just(1)
if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) {
AnyEffect.just(2)
} else {
AnyEffect.just(3)
}
}

var result: [Int] = []
for await value in effect.values {
result.append(value)
}

XCTAssertEqual(result, [1, 2])
}

do {
let effect = AnyEffect<Int>.merge {
AnyEffect.just(1)
if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) {
AnyEffect.just(2)
} else {
AnyEffect.just(3)
}
}

var result: Set<Int> = []
for await value in effect.values {
result.insert(value)
}

XCTAssertEqual(result, [1, 2])
}
}
}