Files
sourcekit-lsp/Sources/SwiftExtensions/PipeAsStringHandler.swift
Alex Hoppen 9618df80a0 Add documentation about each module's purpose and move some files between modules
The purpose of the different modules wasn’t clearly defined, which lead to inconsistent responsibilities between the different modules. Define each module’s purpose and move a few files between modules to satisfy these definitions.

There are a few more larger changes that will need to be made for a fully consistent module structure. These are FIXMEs in the new Modules.md document and I’ll address them in follow-up PRs.
2024-06-25 07:47:45 -07:00

51 lines
2.1 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Foundation
import SwiftExtensions
/// Gathers data from a stdout or stderr pipe. When it has accumulated a full line, calls the handler to handle the
/// string.
public actor PipeAsStringHandler {
/// Queue on which all data from the pipe will be handled. This allows us to have a
/// nonisolated `handle` function but ensure that data gets processed in order.
private let queue = AsyncQueue<Serial>()
private var buffer = Data()
/// The closure that actually handles
private let handler: @Sendable (String) -> Void
public init(handler: @escaping @Sendable (String) -> Void) {
self.handler = handler
}
private func handleDataFromPipeImpl(_ newData: Data) {
self.buffer += newData
while let newlineIndex = self.buffer.firstIndex(of: UInt8(ascii: "\n")) {
// Output a separate log message for every line in the pipe.
// The reason why we don't output multiple lines in a single log message is that
// a) os_log truncates log messages at about 1000 bytes. The assumption is that a single line is usually less
// than 1000 bytes long but if we merge multiple lines into one message, we might easily exceed this limit.
// b) It might be confusing why sometimes a single log message contains one line while sometimes it contains
// multiple.
handler(String(data: self.buffer[...newlineIndex], encoding: .utf8) ?? "<invalid UTF-8>")
buffer = buffer[buffer.index(after: newlineIndex)...]
}
}
public nonisolated func handleDataFromPipe(_ newData: Data) {
queue.async {
await self.handleDataFromPipeImpl(newData)
}
}
}