Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ respond! = \req, _ ->

If you'd like to contribute, check out our [group chat](https://roc.zulipchat.com) and let us know what you're thinking, we're friendly!

## Developing / Building Locally
## Running Locally

If you have cloned this repository and want to run the examples without using a packaged release (...tar.br), you will need to build the platform first by running `roc build.roc`. Run examples with `roc examples/hello.roc` (on linux, add `--linker=legacy`).
If you have cloned this repository and want to run the examples without using a packaged release (...tar.br), you will need to build the platform first by running `roc build.roc`. Run examples with `roc examples/hello-web.roc` (on linux, add `--linker=legacy`).

## Benchmarking

Expand Down
28 changes: 28 additions & 0 deletions ci/expect_scripts/cmd-test.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/expect

# uncomment line below for debugging
# exp_internal 1

set timeout 7

source ./ci/expect_scripts/shared-code.exp

spawn $env(TESTS_DIR)cmd-test


set expected_output [normalize_output {
cat: non_existent.txt: No such file or directory
cat: non_existent.txt: No such file or directory
cat: non_existent.txt: No such file or directory
All tests passed.
Done.
}]

expect $expected_output {
expect eof {
check_exit_and_segfault
}
}

puts stderr "\nExpect script failed: output was not as expected. Diff the output with expected_output in this script. Alternatively, uncomment `exp_internal 1` to debug."
exit 1
12 changes: 7 additions & 5 deletions ci/expect_scripts/command.exp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ spawn $env(EXAMPLES_DIR)command


set expected_output [normalize_output {
Command output: Hi

Command output: BAZ=DUCK
Hello
\{stderr_utf8_lossy: "", stdout_utf8: "Hi
"\}
BAZ=DUCK
FOO=BAR
XYZ=ABC

Yo
cat: non_existent.txt: No such file or directory
Exit code: 1
\{stderr_bytes: \[\], stdout_bytes: \[72, 105, 10\]\}
}]

expect $expected_output {
Expand Down
9 changes: 2 additions & 7 deletions ci/expect_scripts/dir-test.exp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ This will create and manipulate test directories in the current directory.

Testing Dir.create!:
Created directory: test_dir_create
Directory exists: Bool.true
Is a directory: Bool.true
Directory listing: drwxr-xr-x \\d+ \\w+ (\\w+ )? *\\d+ \\w+ +\\d+ \\d+:\\d+ test_dir_create
Creating existing directory result: Expected error
Expand All @@ -30,14 +29,10 @@ test_parent_all/test_child_all/test_grandchild_all

Number of directories created: 3
Expected 3 directories: Bool.true
Creating existing directory with create_all succeeded: Bool.true
Single directory with create_all exists: Bool.true

Testing Dir.delete_empty!:
Empty directory existed before delete: Bool.true
Empty directory exists after delete: Bool.false
Deleting non-empty directory result: Expected error
Non-empty directory still exists: Bool.true
Deleting non-existent directory result: Expected error

Testing Dir.delete_all!:
Expand All @@ -47,13 +42,13 @@ test_complex_for_delete_all/subdir1/file2.txt
test_complex_for_delete_all/subdir2/file4.txt
test_complex_for_delete_all/subdir1/subsubdir/file3.txt
Number of files: 4
Complex directory exists after delete_all: Bool.false
Complex directory is gone after delete_all: Bool.true
Deleting non-existent directory with delete_all result: Expected error

I ran all Dir function tests.

Cleaning up test directories...
Cleanup completed. Directories remaining: 0
Cleanup completed.
Ran all tests.
"]

Expand Down
27 changes: 9 additions & 18 deletions ci/expect_scripts/path-test.exp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Path with replaced extension: test_file.new
Extension replaced: Bool.true

Testing Path file operations:
test_path_bytes.txt exists: Bool.true
Bytes written: \\\[72, 101, 108, 108, 111, 44, 32, 80, 97, 116, 104, 33\\\]
Bytes read: \\\[72, 101, 108, 108, 111, 44, 32, 80, 97, 116, 104, 33\\\]
Bytes match: Bool.true
Expand All @@ -35,32 +34,24 @@ UTF-8 content matches: Bool.true
JSON content: {\"message\":\"Path test\",\"numbers\":\\\[1,2,3\\\]}
JSON contains 'message' field: Bool.true
JSON contains 'numbers' field: Bool.true
File exists before delete: Bool.true
File exists after delete: Bool.false

Testing Path directory operations:
Created directory: drwxr-xr-x \\d+ \\w+ (\\w+ )? *\\d+ \\w+ +\\d+ \\d+:\\d+ test_single_dir
Is a directory: Bool.true
File no longer exists: Bool.true

Testing Path directory operations...
Nested directory structure:
test_parent
test_parent/test_child
test_parent/test_child/test_grandchild

Number of directories created: 3
Directory contents:
total \\d+
dr\[-rwx\]+ +\\d+ \\w+ (\\w+ )? *\\d+ \\w+ +\\d+ \\d+:\\d+ \\.
dr\[-rwx\]+ +\\d+ \\w+ (\\w+ )? *\\d+ \\w+ +\\d+ \\d+:\\d+ \\.\\.
-\[-rwx\]+ +\\d+ \\w+ (\\w+ )? *\\d+ \\w+ +\\d+ \\d+:\\d+ file1\\.txt
-\[-rwx\]+ +\\d+ \\w+ (\\w+ )? *\\d+ \\w+ +\\d+ \\d+:\\d+ file2\\.txt
dr\[-rwx\]+ +\\d+ \\w+ (\\w+ )? *\\d+ \\w+ +\\d+ \\d+:\\d+ subdir

Empty dir exists before delete: Bool.true
Empty dir exists after delete: Bool.false
file1.txt
file2.txt
subdir

Empty dir was deleted: Bool.true
Size before delete_all: \\d+\\w*\\s*test_parent

Parent dir exists after delete_all: Bool.false
Parent dir no longer exists: Bool.true

Testing Path.hard_link!:
Hard link count before: 1
Expand Down Expand Up @@ -104,7 +95,7 @@ Nonexistent path result: Expected error
I ran all Path function tests.

Cleaning up test files...
Files remaining after cleanup: Bool.false
Files deleted successfully: Bool.true
Ran all tests.
"]

Expand Down
10 changes: 5 additions & 5 deletions crates/roc_host/src/roc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,17 +269,17 @@ pub extern "C" fn roc_fx_posix_time() -> roc_std::U128 {
}

#[no_mangle]
pub extern "C" fn roc_fx_command_status(
pub extern "C" fn roc_fx_command_exec_exit_code(
roc_cmd: &roc_command::Command,
) -> RocResult<i32, roc_io_error::IOErr> {
roc_command::command_status(roc_cmd)
roc_command::command_exec_exit_code(roc_cmd)
}

#[no_mangle]
pub extern "C" fn roc_fx_command_output(
pub extern "C" fn roc_fx_command_exec_output(
roc_cmd: &roc_command::Command,
) -> roc_command::OutputFromHost {
roc_command::command_output(roc_cmd)
) -> RocResult<roc_command::OutputFromHostSuccess, RocResult<roc_command::OutputFromHostFailure, roc_io_error::IOErr>> {
roc_command::command_exec_output(roc_cmd)
}

#[no_mangle]
Expand Down
111 changes: 34 additions & 77 deletions examples/command.roc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import pf.Http exposing [Request, Response]
import pf.Cmd
import pf.Utc
import pf.Stdout
import pf.Stderr

# To run this example: check the README.md in this folder

Expand All @@ -13,26 +12,52 @@ Model : {}
init! : {} => Result Model _
init! = |{}|


# Simplest way to execute a command (prints to your terminal).
# See `Cmd.exec!` in `respond!` below.
Cmd.exec!("echo", ["Hello"])?

# To execute and capture the output (stdout, stderr, and exit code) without inheriting your terminal.
output_example!({}) ? |err| OutputExampleFailed(err)
# To execute and capture the output (stdout and stderr) without inheriting your terminal.
cmd_output =
Cmd.new("echo")
|> Cmd.args(["Hi"])
|> Cmd.exec_output!()?

Stdout.line!("${Inspect.to_str(cmd_output)}")?

# To run a command with an environment variable.
env_example!({}) ? |err| EnvExampleFailed(err)
# To run a command with environment variables.
Cmd.new("env")
|> Cmd.clear_envs # You probably don't need to clear all other environment variables, this is just an example.
|> Cmd.env("FOO", "BAR")
|> Cmd.envs([("BAZ", "DUCK"), ("XYZ", "ABC")]) # Set multiple environment variables at once with `envs`
|> Cmd.args(["-v"])
|> Cmd.exec_cmd!()?

# To execute and just get the exit code (prints to your terminal).
status_example!({}) ? |err| StatusExampleFailed(err)
# Prefer using `exec!` or `exec_cmd!`.
exit_code =
Cmd.new("cat")
|> Cmd.args(["non_existent.txt"])
|> Cmd.exec_exit_code!()?

Stdout.line!("Exit code: ${Num.to_str(exit_code)}")?

# To execute and capture the output (stdout and stderr) in the original form as bytes without inheriting your terminal.
# Prefer using `exec_output!`.
cmd_output_bytes =
Cmd.new("echo")
|> Cmd.args(["Hi"])
|> Cmd.exec_output_bytes!()?

Stdout.line!("${Inspect.to_str(cmd_output_bytes)}")?

Ok({})

respond! : Request, Model => Result Response [EchoCmdFailed(_)]
respond! : Request, Model => Result Response [ExecFailed(_), FailedToGetExitCode(_)]
respond! = |req, _|
datetime = Utc.to_iso_8601(Utc.now!({}))

# Log request date, method and url using echo
Cmd.exec!("echo", ["${datetime} ${Inspect.to_str(req.method)} ${req.uri}"]) ? |err| EchoCmdFailed(err)
Cmd.exec!("echo", ["${datetime} ${Inspect.to_str(req.method)} ${req.uri}"])?

Ok(
{
Expand All @@ -42,71 +67,3 @@ respond! = |req, _|
},
)

# Execute command and capture the output (stdout, stderr, and exit code)
output_example! : {} => Result {} _
output_example! = |{}|

cmd_output =
Cmd.new("echo")
|> Cmd.args(["Hi"])
|> Cmd.output!

print_output!(cmd_output)


print_output! : Cmd.Output => Result {} _
print_output! = |cmd_output|


when cmd_output.status is
Ok(0) ->
stdout_utf8 = Str.from_utf8(cmd_output.stdout)?
Stdout.line!("Command output: ${stdout_utf8}")

Ok(exit_code) ->
stdout_utf8 = Str.from_utf8_lossy(cmd_output.stdout)
stderr_utf8 = Str.from_utf8_lossy(cmd_output.stderr)
err_data =
"""
Command failed:
- exit code: ${Num.to_str(exit_code)}
- stdout: ${stdout_utf8}
- stderr: ${stderr_utf8}
"""

Stderr.line!(err_data)

Err(err) ->
Stderr.line!("Failed to get exit code for command, error: ${Inspect.to_str(err)}")


# Run command with an environment variable
env_example! : {} => Result {} _
env_example! = |{}|

cmd_output =
Cmd.new("env")
|> Cmd.clear_envs # You probably don't need to clear all other environment variables, this is just an example.
|> Cmd.env("FOO", "BAR")
|> Cmd.envs([("BAZ", "DUCK"), ("XYZ", "ABC")]) # Set multiple environment variables at once with `envs`
|> Cmd.args(["-v"])
|> Cmd.output!

print_output!(cmd_output)

# Execute command and capture the exit code
status_example! : {} => Result {} _
status_example! = |{}|
cmd_result =
Cmd.new("echo")
|> Cmd.args(["Yo"])
|> Cmd.status!

when cmd_result is
Ok(0) -> Ok({})

Ok(exit_code) ->
Stderr.line!("Command failed with exit code: ${Num.to_str(exit_code)}")

Err(err) ->
Stderr.line!("Failed to get exit code for command, error: ${Inspect.to_str(err)}")
Loading