tomo/examples/commands/commands.tm

92 lines
3.5 KiB
Plaintext
Raw Normal View History

# Functions for running system commands
use ./commands.c
use -lunistring
extern run_command : func(exe:Text, args:[Text], env:{Text=Text}, input:[Byte]?, output:&[Byte]?, error:&[Byte]? -> Int32)
extern command_by_line : func(exe:Text, args:[Text], env:{Text=Text} -> func(->Text?)?)
2025-04-06 13:07:23 -07:00
enum ExitType(Exited(status:Int32), Signaled(signal:Int32), Failed)
func succeeded(e:ExitType -> Bool)
when e is Exited(status) return (status == 0)
else return no
2025-03-24 12:20:19 -07:00
2025-04-06 13:07:23 -07:00
func or_fail(e:ExitType, message:Text?=none)
if not e.succeeded()
2025-03-24 12:23:29 -07:00
fail(message or "Program failed: $e")
2025-04-06 13:07:23 -07:00
struct ProgramResult(stdout:[Byte], stderr:[Byte], exit_type:ExitType)
func or_fail(r:ProgramResult, message:Text?=none -> ProgramResult)
when r.exit_type is Exited(status)
if status == 0
return r
2025-04-06 13:07:23 -07:00
else fail(message or "Program failed: $r")
2025-03-24 12:23:29 -07:00
fail(message or "Program failed: $r")
2025-04-06 13:07:23 -07:00
func output_text(r:ProgramResult, trim_newline=yes -> Text?)
when r.exit_type is Exited(status)
if status == 0
if text := Text.from_bytes(r.stdout)
if trim_newline
text = text.without_suffix(\n)
return text
2025-04-06 13:07:23 -07:00
else return none
return none
2025-04-06 13:07:23 -07:00
func error_text(r:ProgramResult -> Text?)
when r.exit_type is Exited(status)
if status == 0
return Text.from_bytes(r.stderr)
2025-04-06 13:07:23 -07:00
else return none
return none
2025-04-06 13:07:23 -07:00
func succeeded(r:ProgramResult -> Bool)
when r.exit_type is Exited(status)
return (status == 0)
2025-04-06 13:07:23 -07:00
else
return no
2025-04-06 13:07:23 -07:00
struct Command(command:Text, args:[Text]=[], env:{Text=Text}={})
func from_path(path:Path, args:[Text]=[], env:{Text=Text}={} -> Command)
2025-03-17 18:22:10 -07:00
return Command(Text(path), args, env)
2025-04-06 13:07:23 -07:00
func result(command:Command, input="", input_bytes:[Byte]=[] -> ProgramResult)
if input.length > 0
(&input_bytes).insert_all(input.bytes())
stdout : [Byte]
stderr : [Byte]
status := run_command(command.command, command.args, command.env, input_bytes, &stdout, &stderr)
2025-04-06 13:07:23 -07:00
if inline C : Bool { WIFEXITED(_$status) }
return ProgramResult(stdout, stderr, ExitType.Exited(inline C : Int32 { WEXITSTATUS(_$status) }))
2025-04-06 13:07:23 -07:00
if inline C : Bool { WIFSIGNALED(_$status) }
return ProgramResult(stdout, stderr, ExitType.Signaled(inline C : Int32 { WTERMSIG(_$status) }))
return ProgramResult(stdout, stderr, ExitType.Failed)
2025-04-06 13:07:23 -07:00
func run(command:Command, -> ExitType)
status := run_command(command.command, command.args, command.env, none, none, none)
2025-04-06 13:07:23 -07:00
if inline C : Bool { WIFEXITED(_$status) }
return ExitType.Exited(inline C : Int32 { WEXITSTATUS(_$status) })
2025-04-06 13:07:23 -07:00
if inline C : Bool { WIFSIGNALED(_$status) }
return ExitType.Signaled(inline C : Int32 { WTERMSIG(_$status) })
return ExitType.Failed
2025-04-06 13:07:23 -07:00
func get_output(command:Command, input="", trim_newline=yes -> Text?)
return command.result(input=input).output_text(trim_newline=trim_newline)
2025-04-06 13:07:23 -07:00
func get_output_bytes(command:Command, input="", input_bytes:[Byte]=[] -> [Byte]?)
result := command.result(input=input, input_bytes=input_bytes)
2025-04-06 13:07:23 -07:00
when result.exit_type is Exited(status)
if status == 0 return result.stdout
return none
2025-04-06 13:07:23 -07:00
else return none
2025-04-06 13:07:23 -07:00
func by_line(command:Command -> func(->Text?)?)
2025-03-19 13:17:44 -07:00
return command_by_line(command.command, command.args, command.env)