Skip to content

Commit 3d63dd6

Browse files
authored
Improve command example (#391)
* improve command example * improve comments * correction * show envs fn too
1 parent d26267e commit 3d63dd6

File tree

4 files changed

+122
-35
lines changed

4 files changed

+122
-35
lines changed

ci/expect_scripts/command.exp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@ source ./ci/expect_scripts/shared-code.exp
1010
spawn $env(EXAMPLES_DIR)command
1111

1212

13-
expect -exact "EXEC\r\nFOO=BAR\r\nBAZ=DUCK\r\nFOO=BAR\r\n" {
14-
expect eof {
15-
check_exit_and_segfault
13+
expect "Hello\r\n" {
14+
expect "Command output: Hi\r\n" {
15+
expect "Command output: BAZ=DUCK\r\n" {
16+
expect "FOO=BAR\r\n" {
17+
expect "XYZ=ABC\r\n" {
18+
expect "Yo\r\n" {
19+
expect eof {
20+
check_exit_and_segfault
21+
}
22+
}
23+
}
24+
}
25+
}
1626
}
1727
}
1828

examples/command.roc

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
app [main!] { pf: platform "../platform/main.roc" }
22

33
import pf.Stdout
4+
import pf.Stderr
45
import pf.Cmd
56
import pf.Arg exposing [Arg]
67

@@ -11,48 +12,85 @@ import pf.Arg exposing [Arg]
1112
main! : List Arg => Result {} _
1213
main! = |_args|
1314

14-
# Simplest way to execute a command
15-
exec_example!({})?
15+
# Simplest way to execute a command (prints to your terminal).
16+
Cmd.exec!("echo", ["Hello"]) ? |err| EchoHelloFailed(err)
1617

17-
# To execute and capture the output (includes stderr)
18-
output_example!({})?
18+
# To execute and capture the output (stdout, stderr, and exit code) without inheriting your terminal.
19+
output_example!({}) ? |err| OutputExampleFailed(err)
1920

20-
# To execute and get the exit code
21-
status_example!({})?
21+
# To run a command with an environment variable.
22+
env_example!({}) ? |err| EnvExampleFailed(err)
23+
24+
# To execute and just get the exit code (prints to your terminal).
25+
status_example!({}) ? |err| StatusExampleFailed(err)
2226

2327
Ok({})
2428

25-
exec_example! : {} => Result {} _
26-
exec_example! = |{}| Cmd.exec!("echo", ["EXEC"])
29+
# Execute command and capture the output (stdout, stderr, and exit code)
30+
output_example! : {} => Result {} _
31+
output_example! = |{}|
2732

28-
# Run "env" with verbose option, clear all environment variables, and pass in
29-
# "FOO" and "BAZ".
30-
status_example! : {} => Result {} _
31-
status_example! = |{}|
32-
result =
33-
Cmd.new("env")
34-
|> Cmd.arg("-v")
35-
|> Cmd.clear_envs
36-
|> Cmd.envs([("FOO", "BAR"), ("BAZ", "DUCK")])
37-
|> Cmd.status!
33+
cmd_output =
34+
Cmd.new("echo")
35+
|> Cmd.args(["Hi"])
36+
|> Cmd.output!
3837

39-
when result is
40-
Ok(exit_code) if exit_code == 0 -> Ok({})
41-
Ok(exit_code) -> Stdout.line!("Child exited with non-zero code: ${Num.to_str(exit_code)}")
42-
Err(err) -> Stdout.line!("Error executing command: ${Inspect.to_str(err)}")
38+
print_output!(cmd_output)
39+
40+
41+
print_output! : Cmd.Output => Result {} _
42+
print_output! = |cmd_output|
4343

44-
# Run "env" with verbose option, clear all environment variables, and pass in
45-
# only as an environment variable "FOO"
46-
output_example! : {} => Result {} _
47-
output_example! = |{}|
4844

49-
output =
45+
when cmd_output.status is
46+
Ok(0) ->
47+
stdout_utf8 = Str.from_utf8(cmd_output.stdout)?
48+
Stdout.line!("Command output: ${stdout_utf8}")
49+
50+
Ok(exit_code) ->
51+
stdout_utf8 = Str.from_utf8_lossy(cmd_output.stdout)
52+
stderr_utf8 = Str.from_utf8_lossy(cmd_output.stderr)
53+
err_data =
54+
"""
55+
Command failed:
56+
- exit code: ${Num.to_str(exit_code)}
57+
- stdout: ${stdout_utf8}
58+
- stderr: ${stderr_utf8}
59+
"""
60+
61+
Stderr.line!(err_data)
62+
63+
Err(err) ->
64+
Stderr.line!("Failed to get exit code for command, error: ${Inspect.to_str(err)}")
65+
66+
67+
# Run command with an environment variable
68+
env_example! : {} => Result {} _
69+
env_example! = |{}|
70+
71+
cmd_output =
5072
Cmd.new("env")
51-
|> Cmd.clear_envs
73+
|> Cmd.clear_envs # You probably don't need to clear all other environment variables, this is just an example.
5274
|> Cmd.env("FOO", "BAR")
75+
|> Cmd.envs([("BAZ", "DUCK"), ("XYZ", "ABC")]) # Set multiple environment variables at once with `envs`
5376
|> Cmd.args(["-v"])
5477
|> Cmd.output!
5578

56-
msg = Str.from_utf8(output.stdout) |> Result.with_default("Failed to decode stdout")
79+
print_output!(cmd_output)
80+
81+
# Execute command and capture the exit code
82+
status_example! : {} => Result {} _
83+
status_example! = |{}|
84+
cmd_result =
85+
Cmd.new("echo")
86+
|> Cmd.args(["Yo"])
87+
|> Cmd.status!
88+
89+
when cmd_result is
90+
Ok(0) -> Ok({})
91+
92+
Ok(exit_code) ->
93+
Stderr.line!("Command failed with exit code: ${Num.to_str(exit_code)}")
5794

58-
Stdout.write!(msg)
95+
Err(err) ->
96+
Stderr.line!("Failed to get exit code for command, error: ${Inspect.to_str(err)}")

platform/Cmd.roc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ Cmd := InternalCmd.Command
3232
##
3333
Output : InternalCmd.Output
3434

35+
# This hits a compiler bug: Alias `6.IdentId(11)` not registered in delayed aliases! ...
36+
# ## Converts output into a utf8 string. Invalid utf8 sequences in stderr are ignored.
37+
# to_str : Output -> Result Str [BadUtf8 { index : U64, problem : Str.Utf8Problem }]
38+
# to_str = |output|
39+
# InternalCmd.output_to_str(output)
40+
41+
# ## Converts output into a utf8 string, ignoring any invalid utf8 sequences.
42+
# to_str_lossy : Output -> Str
43+
# to_str_lossy = |output|
44+
# InternalCmd.output_to_str_lossy(output)
45+
3546
## Create a new command to execute the given program in a child process.
3647
new : Str -> Cmd
3748
new = |program|
@@ -119,15 +130,15 @@ output! = |@Cmd(cmd)|
119130
Host.command_output!(cmd)
120131
|> InternalCmd.from_host_output
121132

122-
## Execute command and inherit stdin, stdout and stderr from parent
133+
## Execute command and inherit stdin, stdout and stderr from parent. Returns the exit code.
123134
##
124135
status! : Cmd => Result I32 [CmdStatusErr InternalIOErr.IOErr]
125136
status! = |@Cmd(cmd)|
126137
Host.command_status!(cmd)
127138
|> Result.map_err(InternalIOErr.handle_err)
128139
|> Result.map_err(CmdStatusErr)
129140

130-
## Execute command and inherit stdin, stdout and stderr from parent
141+
## Simplest way to execute a command while inheriting stdin, stdout and stderr from parent.
131142
##
132143
## ```
133144
## # Call echo to print "hello world"

platform/InternalCmd.roc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,34 @@ Output : {
2020
stderr : List U8,
2121
}
2222

23+
# This hits a compiler bug: Alias `6.IdentId(11)` not registered in delayed aliases! ...
24+
# output_to_str : Output -> Result Str [BadUtf8 { index : U64, problem : Str.Utf8Problem }]
25+
# output_to_str = |cmd_output|
26+
# stdout_utf8 = Str.from_utf8(cmd_output.stdout)?
27+
# stderr_utf8 = Str.from_utf8_lossy(cmd_output.stderr)
28+
29+
# Ok(
30+
# output_str_template(cmd_output.status, stdout_utf8, stderr_utf8)
31+
# )
32+
33+
# output_to_str_lossy : Output -> Str
34+
# output_to_str_lossy = |cmd_output|
35+
# stdout_utf8 = Str.from_utf8_lossy(cmd_output.stdout)
36+
# stderr_utf8 = Str.from_utf8_lossy(cmd_output.stderr)
37+
38+
39+
# output_str_template(cmd_output.status, stdout_utf8, stderr_utf8)
40+
41+
# output_str_template : Result I32 InternalIOErr.IOErr, Str, Str -> Str
42+
# output_str_template = |status, stdout_utf8, stderr_utf8|
43+
# """
44+
# Output {
45+
# status: ${Inspect.to_str(status)}
46+
# stdout: ${stdout_utf8}
47+
# stderr: ${stderr_utf8}
48+
# }
49+
# """
50+
2351
from_host_output : OutputFromHost -> Output
2452
from_host_output = |{ status, stdout, stderr }| {
2553
status: Result.map_err(status, InternalIOErr.handle_err),

0 commit comments

Comments
 (0)