aerospace fork
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169import Commonimport Darwinimport Foundationimport Network
let usage = """ USAGE: \(CommandLine.arguments.first ?? "deewm") [-h|--help] [-v|--version] <subcommand> [<args>...]
SUBCOMMANDS: \(subcommandDescriptions.sortedBy { $0[0] }.toPaddingTable(columnSeparator: " ").joined(separator: "\n")) """
@mainstruct Main { static func main() async { let args = CommandLine.arguments.slice(1...) ?? []
if args.isEmpty { exit(EXIT_CODE_TWO, err: usage) } if args.first == "--help" || args.first == "-h" { exit(EXIT_CODE_ZERO, out: usage) }
if args.first == "--version" || args.first == "-v" { let connection = NWConnection(to: NWEndpoint.unix(path: socketPath), using: .tcp) let serverVersionAndHash: String? switch await connection.initConnection().error { case nil: let ans = await run(connection, [], stdin: "", windowId: nil, workspace: nil, failExitCode: EXIT_CODE_TWO) serverVersionAndHash = ans.serverVersionAndHash case .nwError(let e): eprint(e.localizedDescription) serverVersionAndHash = nil case .customError(let msg): exit(EXIT_CODE_TWO, err: msg) } print( """ deewm CLI client version: \(cliClientVersionAndHash) DeeWM.app server version: \(serverVersionAndHash ?? "Unknown. The server is not responding") """, ) if serverVersionAndHash != nil && cliClientVersionAndHash != serverVersionAndHash { eprint( """ Warning: DeeWM client/server versions don't match. Possible fixes: - Restart DeeWM.app (server restart is required after each update) - Reinstall and restart DeeWM (corrupted installation) """, ) } exit(EXIT_CODE_ZERO) }
let parsedArgs: any CmdArgs switch parseCmdArgs(args) { // Optimizations case .cmd(_ as TrueCmdArgs): exit(ConditionalExitCode._true.rawValue) case .cmd(_ as FalseCmdArgs): exit(ConditionalExitCode._false.rawValue)
case .cmd(let _parsedArgs): parsedArgs = _parsedArgs case .help(let help): exit(EXIT_CODE_ZERO, out: help) case .failure(let e): exit(e.exitCode, err: e.msg) }
let failExitCode = parsedArgs.failExitCode
let connection = NWConnection(to: NWEndpoint.unix(path: socketPath), using: .tcp)
switch await connection.initConnection().error { case nil: break case .customError(let msg): exit(failExitCode, err: msg) case .nwError(let e): exit(failExitCode, err: "Can't connect to DeeWM server. Is DeeWM.app running?\n\(e.localizedDescription)") }
var stdin = "" if parsedArgs.commonState.explicitStdinFlag == true || // todo: drop after a couple of versions (parsedArgs.commonState.explicitStdinFlag != false && (parsedArgs is WorkspaceCmdArgs && (parsedArgs as! WorkspaceCmdArgs).target.val.isRelatve || parsedArgs is MoveNodeToWorkspaceCmdArgs && (parsedArgs as! MoveNodeToWorkspaceCmdArgs).target.val.isRelatve) && hasStdin()) { if parsedArgs is WorkspaceCmdArgs && parsedArgs.commonState.explicitStdinFlag == nil || parsedArgs is MoveNodeToWorkspaceCmdArgs && parsedArgs.commonState.explicitStdinFlag == nil { exit( failExitCode, err: """ ERROR: Implicit stdin is detected (stdin is not TTY). Implicit stdin was forbidden in AeroSpace v0.20.0. 1. Please supply '--stdin' flag to make stdin explicit and preserve old AeroSpace behavior 2. You can also use '--no-stdin' flag to behave as if no stdin was supplied Breaking change issue: https://github.com/nikitabobko/AeroSpace/issues/1683 """, ) } var index = 0 while let line = readLine(strippingNewline: false) { stdin += line index += 1 if index > 1000 { exit(failExitCode, err: "stdin number of lines limit is exceeded") } } }
let windowId = ProcessInfo.processInfo.environment[DEEWM_WINDOW_ID].flatMap(UInt32.init) let workspace = ProcessInfo.processInfo.environment[DEEWM_WORKSPACE]
// Handle subscribe command specially if parsedArgs is SubscribeCmdArgs { await runSubscribe(connection, args, windowId: windowId, workspace: workspace, failExitCode: parsedArgs.failExitCode) exit(EXIT_CODE_ZERO) // Should not reach here }
let ans = await run(connection, args, stdin: stdin, windowId: windowId, workspace: workspace, failExitCode: failExitCode)
if !ans.stdout.isEmpty { print(ans.stdout) } if !ans.stderr.isEmpty { eprint(ans.stderr) } if ans.exitCode != EXIT_CODE_ZERO && ans.serverVersionAndHash != cliClientVersionAndHash { eprint( """ Warning: DeeWM client/server versions don't match - deewm CLI client version: \(cliClientVersionAndHash) - DeeWM.app server version: \(ans.serverVersionAndHash) Possible fixes: - Restart DeeWM.app (server restart is required after each update) - Reinstall and restart DeeWM (corrupted installation) """, ) } exit(ans.exitCode) }}
func runSubscribe(_ connection: NWConnection, _ args: StrArrSlice, windowId: UInt32?, workspace: String?, failExitCode: Int32) async { if let e = await connection.writeAtomic(ClientRequest(args: args.toArray(), stdin: "", windowId: windowId, workspace: workspace)).error { exit(failExitCode, err: "Failed to write to server socket: \(e)") }
while true { switch await connection.readNonAtomic() { case .success(let data): if let str = String(data: data, encoding: .utf8) { print(str) unsafe fflush(stdout) } else { exit(failExitCode, err: "Can't convert bytes to utf8 String") } case .failure(let e): exit(failExitCode, err: "runSubscribe error: \(e)") } }}
func run(_ connection: NWConnection, _ args: StrArrSlice, stdin: String, windowId: UInt32?, workspace: String?, failExitCode: Int32) async -> ServerAnswer { if let e = await connection.writeAtomic(ClientRequest(args: args.toArray(), stdin: stdin, windowId: windowId, workspace: workspace)).error { exit(failExitCode, err: "Failed to write to server socket: \(e)") }
switch await connection.readNonAtomic() { case .success(let answer): return (try? JSONDecoder().decode(ServerAnswer.self, from: answer)) ?? exitT(EXIT_CODE_TWO, err: "Failed to parse server response: \(String(data: answer, encoding: .utf8).prettyDescription)") case .failure(let error): exit(failExitCode, err: "Failed to read from server socket: \(error)") }}