aboutsummaryrefslogtreecommitdiff
path: root/Sources/phbar/Models/Runner.swift
diff options
context:
space:
mode:
authorTommaso Negri <me@tommasongr.com>2026-07-27 16:18:34 +0200
committerTommaso Negri <me@tommasongr.com>2026-07-27 16:18:34 +0200
commit535b42a7b8304340182ca1367aa42f047eb06bff (patch)
tree88e15512f874a00886fc7b97861feb5e92c6aaf5 /Sources/phbar/Models/Runner.swift
parent6d165c74a5a6b471c155b12ab15ef833d6b2aa36 (diff)
downloadbar-535b42a7b8304340182ca1367aa42f047eb06bff.tar.gz
bar-535b42a7b8304340182ca1367aa42f047eb06bff.zip
scaffold basic structure
Diffstat (limited to 'Sources/phbar/Models/Runner.swift')
-rw-r--r--Sources/phbar/Models/Runner.swift45
1 files changed, 45 insertions, 0 deletions
diff --git a/Sources/phbar/Models/Runner.swift b/Sources/phbar/Models/Runner.swift
new file mode 100644
index 0000000..a98d34a
--- /dev/null
+++ b/Sources/phbar/Models/Runner.swift
@@ -0,0 +1,45 @@
+import Foundation
+
+/// Runs a shell command and returns its stdout as cells (one per line).
+///
+/// Blocking — always call from a background context (e.g. `Task.detached`).
+enum Runner {
+ static func run(_ command: String, env: [String: String] = [:]) -> [String] {
+ let process = Process()
+ process.executableURL = URL(fileURLWithPath: "/bin/sh")
+ process.arguments = ["-c", command]
+
+ var environment = ProcessInfo.processInfo.environment
+ environment.merge(env) { _, new in new }
+ process.environment = environment
+
+ let stdout = Pipe()
+ let stderr = Pipe()
+ process.standardOutput = stdout
+ process.standardError = stderr
+
+ guard let _ = try? process.run() else {
+ return ["(error)"]
+ }
+
+ let data = stdout.fileHandleForReading.readDataToEndOfFile()
+ let errData = stderr.fileHandleForReading.readDataToEndOfFile()
+ process.waitUntilExit()
+
+ if process.terminationStatus != 0 {
+ let reason = String(data: errData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
+ FileHandle.standardError.write("phbar: `\(command)` failed: \(reason ?? "exit \(process.terminationStatus)")\n".data(using: .utf8)!)
+ }
+
+ return parse(String(data: data, encoding: .utf8) ?? "")
+ }
+
+ /// Split output into cells: one per line. A single trailing newline (from a
+ /// final `\n`) is dropped; blank lines elsewhere are kept so a script can
+ /// emit intentional gaps.
+ static func parse(_ text: String) -> [String] {
+ var lines = text.components(separatedBy: "\n")
+ if lines.last == "" { lines.removeLast() }
+ return lines
+ }
+}