File: Validator.swift

package info (click to toggle)
swiftlang 6.1.3-4
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 2,791,644 kB
  • sloc: cpp: 9,901,738; ansic: 2,201,433; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (117 lines) | stat: -rw-r--r-- 3,074 bytes parent folder | download | duplicates (2)
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import AsyncAlgorithms

public struct Validator<Element: Sendable>: Sendable {
  private enum Ready {
    case idle
    case ready
    case pending(UnsafeContinuation<Void, Never>)
  }
  
  private struct State: Sendable {
    var collected = [Element]()
    var failure: Error?
    var ready: Ready = .idle
  }
  
  private struct Envelope<Contents>: @unchecked Sendable {
    var contents: Contents
  }
  
  private let state = ManagedCriticalState(State())

  private func ready(_ apply: (inout State) -> Void) {
    state.withCriticalRegion { state -> UnsafeContinuation<Void, Never>? in
      apply(&state)
      switch state.ready {
      case .idle:
        state.ready = .ready
        return nil
      case .pending(let continuation):
        state.ready = .idle
        return continuation
      case .ready:
        return nil
      }
    }?.resume()
  }
  
  internal func step() async {
    await withUnsafeContinuation { (continuation: UnsafeContinuation<Void, Never>) in
      state.withCriticalRegion { state -> UnsafeContinuation<Void, Never>? in
        switch state.ready {
        case .ready:
          state.ready = .idle
          return continuation
        case .idle:
          state.ready = .pending(continuation)
          return nil
        case .pending:
          fatalError()
        }
      }?.resume()
    }
  }

  let onEvent: (@Sendable (Result<Element?, Error>) async -> Void)?
  
  init(onEvent: @Sendable @escaping (Result<Element?, Error>) async -> Void) {

    self.onEvent = onEvent
  }
  
  public init() {
    self.onEvent = nil
  }
  
  public func test<S: AsyncSequence>(_ sequence: S, onFinish: @Sendable @escaping (inout S.AsyncIterator) async -> Void) where S.Element == Element {
    let envelope = Envelope(contents: sequence)
    Task {
      var iterator = envelope.contents.makeAsyncIterator()
      ready { _ in }
      do {
        while let item = try await iterator.next() {
          await onEvent?(.success(item))
          ready { state in
            state.collected.append(item)
          }
        }
        await onEvent?(.success(nil))
      } catch {
        await onEvent?(.failure(error))
        ready { state in
          state.failure = error
        }
      }
      ready { _ in }
      await onFinish(&iterator)
    }
  }
  
  public func validate() async -> [Element] {
    await step()
    return current
  }
  
  public var current: [Element] {
    return state.withCriticalRegion { state in
      return state.collected
    }
  }
  
  public var failure: Error? {
    return state.withCriticalRegion { state in
      return state.failure
    }
  }
}