Flow_Browser/PrivateBrowser/Pods/PromiseKit/Extensions/Foundation/Sources/Process+Promise.swift
2024-04-17 14:43:19 +08:00

191 lines
5.8 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
#if !PMKCocoaPods
import PromiseKit
#endif
#if os(macOS)
/**
To import the `Process` category:
use_frameworks!
pod "PromiseKit/Foundation"
Or `Process` is one of the categories imported by the umbrella pod:
use_frameworks!
pod "PromiseKit"
And then in your sources:
import PromiseKit
*/
extension Process {
/**
Launches the receiver and resolves when it exits.
let proc = Process()
proc.launchPath = "/bin/ls"
proc.arguments = ["/bin"]
proc.launch(.promise).compactMap { std in
String(data: std.out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
}.then { stdout in
print(str)
}
*/
public func launch(_: PMKNamespacer) -> Promise<(out: Pipe, err: Pipe)> {
let (stdout, stderr) = (Pipe(), Pipe())
do {
standardOutput = stdout
standardError = stderr
#if swift(>=4.0)
if #available(OSX 10.13, *) {
try run()
} else if let path = launchPath, FileManager.default.isExecutableFile(atPath: path) {
launch()
} else {
throw PMKError.notExecutable(launchPath)
}
#else
guard let path = launchPath, FileManager.default.isExecutableFile(atPath: path) else {
throw PMKError.notExecutable(launchPath)
}
launch()
#endif
} catch {
return Promise(error: error)
}
var q: DispatchQueue {
if #available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) {
return DispatchQueue.global(qos: .default)
} else {
return DispatchQueue.global(priority: .default)
}
}
return Promise { seal in
q.async {
self.waitUntilExit()
guard self.terminationReason == .exit, self.terminationStatus == 0 else {
let stdoutData = try? self.readDataFromPipe(stdout)
let stderrData = try? self.readDataFromPipe(stderr)
let stdoutString = stdoutData.flatMap { (data: Data) -> String? in String(data: data, encoding: .utf8) }
let stderrString = stderrData.flatMap { (data: Data) -> String? in String(data: data, encoding: .utf8) }
return seal.reject(PMKError.execution(process: self, standardOutput: stdoutString, standardError: stderrString))
}
seal.fulfill((stdout, stderr))
}
}
}
private func readDataFromPipe(_ pipe: Pipe) throws -> Data {
let handle = pipe.fileHandleForReading
defer { handle.closeFile() }
// Someday, NSFileHandle will probably be updated with throwing equivalents to its read and write methods,
// as NSTask has, to avoid raising exceptions and crashing the app.
// Unfortunately that day has not yet come, so use the underlying BSD calls for now.
let fd = handle.fileDescriptor
let bufsize = 1024 * 8
let buf = UnsafeMutablePointer<UInt8>.allocate(capacity: bufsize)
#if swift(>=4.1)
defer { buf.deallocate() }
#else
defer { buf.deallocate(capacity: bufsize) }
#endif
var data = Data()
while true {
let bytesRead = read(fd, buf, bufsize)
if bytesRead == 0 {
break
}
if bytesRead < 0 {
throw POSIXError.Code(rawValue: errno).map { POSIXError($0) } ?? CocoaError(.fileReadUnknown)
}
data.append(buf, count: bytesRead)
}
return data
}
/**
The error generated by PromiseKits `Process` extension
*/
public enum PMKError {
/// NOT AVAILABLE ON 10.13 and above because Apple provide this error handling themselves
case notExecutable(String?)
case execution(process: Process, standardOutput: String?, standardError: String?)
}
}
extension Process.PMKError: LocalizedError {
public var errorDescription: String? {
switch self {
case .notExecutable(let path?):
return "File not executable: \(path)"
case .notExecutable(nil):
return "No launch path specified"
case .execution(process: let task, standardOutput: _, standardError: _):
return "Failed executing: `\(task)` (\(task.terminationStatus))."
}
}
}
public extension Promise where T == (out: Pipe, err: Pipe) {
func print() -> Promise<T> {
return tap { result in
switch result {
case .fulfilled(let raw):
let stdout = String(data: raw.out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
let stderr = String(data: raw.err.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
Swift.print("stdout: `\(stdout ?? "")`")
Swift.print("stderr: `\(stderr ?? "")`")
case .rejected(let err):
Swift.print(err)
}
}
}
}
extension Process {
/// Provided because Foundations is USELESS
open override var description: String {
let launchPath = self.launchPath ?? "$0"
var args = [launchPath]
arguments.flatMap{ args += $0 }
return args.map { arg in
let contains: Bool
#if swift(>=3.2)
contains = arg.contains(" ")
#else
contains = arg.characters.contains(" ")
#endif
if contains {
return "\"\(arg)\""
} else if arg == "" {
return "\"\""
} else {
return arg
}
}.joined(separator: " ")
}
}
#endif