Skip to content

Instantly share code, notes, and snippets.

@maximkrouk
Last active May 30, 2020 14:32
Show Gist options
  • Save maximkrouk/24a114058187c0cc94a54929f4ba6b07 to your computer and use it in GitHub Desktop.
Save maximkrouk/24a114058187c0cc94a54929f4ba6b07 to your computer and use it in GitHub Desktop.
Class for stdout redirection
import Foundation
import Combine
class REPL {
private static let shared = REPL()
public static var publisher: AnyPublisher<String, Never> {
shared.publisher.eraseToAnyPublisher()
}
public typealias Publisher = PassthroughSubject<String, Never>
public let publisher = Publisher()
private let stdOut = Pipe()
private var buffer = Buffer()
private init() { setup() }
public func setup() {
printWarning()
setvbuf(stdout, nil, _IONBF, 0)
dup2(stdOut.fileHandleForWriting.fileDescriptor, STDOUT_FILENO)
stdOut.fileHandleForReading.readabilityHandler = { [weak self] handle in
guard
let self = self,
let output = self.buffer.append(handle.availableData)
else { return }
self.publisher.send(output)
}
}
private func printWarning(
file: String = #file,
line: UInt = #line,
function: String = #function
) {
print("⚠️ WARNING: STDOUT WILL BE OVERRIDEN BY REPL INSTANCE")
print("file:", file)
print("line:", line)
print("function:", function)
print("✅ It's fine if you expect it to happen.")
print("Output will be redirected to \(stdOut)")
print("You can read it from this fileHandle:", stdOut.fileHandleForReading)
}
}
extension REPL {
private struct Buffer {
private var storage = Data()
mutating func append(_ data: Data) -> String? {
storage.append(data)
if let string = String(data: storage, encoding: .utf8),
string.last?.isNewline == true
{
buffer.removeAll()
return string
}
return nil
}
}
}
@maximkrouk
Copy link
Author

maximkrouk commented Mar 30, 2020

Removed stderr redirection due to fatal errors, bad access and other system errors, which is inconvenient for the case, when app crashes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment