diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 771f1801..1e0da34c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: - name: Install Zig uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f # ratchet:mlugg/setup-zig@v2.0.5 with: - version: 0.15.2 + version: 0.16.0 - name: Install expect (Ubuntu) if: startsWith(matrix.os, 'ubuntu-') @@ -50,4 +50,4 @@ jobs: uses: roc-lang/roc/.github/actions/flaky-retry@main with: command: ./ci/all_tests.sh - error_string_contains: "error: unable" + error_string_contains: "error: (unable|invalid HTTP response)|HttpConnectionClosing" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3570cd43..c4fcd292 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: - name: Install Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.2 + version: 0.16.0 - name: Install Rust with cross-compilation targets uses: dtolnay/rust-toolchain@stable @@ -43,19 +43,7 @@ jobs: targets: x86_64-apple-darwin,aarch64-apple-darwin,x86_64-unknown-linux-musl,aarch64-unknown-linux-musl - name: Build Roc from pinned commit - run: | - echo "Building roc from pinned commit..." - ROC_COMMIT=$(python3 ci/get_roc_commit.py) - - git init roc-src - cd roc-src - git remote add origin https://github.com/roc-lang/roc - git fetch --depth 1 origin "$ROC_COMMIT" - git checkout --detach "$ROC_COMMIT" - - zig build roc - - echo "$(pwd)/zig-out/bin" >> "$GITHUB_PATH" + run: ./ci/build_pinned_roc.sh - name: Build platform for all targets run: ./build.sh --all @@ -111,7 +99,7 @@ jobs: - name: Install Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.2 + version: 0.16.0 - name: Install Rust uses: dtolnay/rust-toolchain@stable @@ -125,19 +113,7 @@ jobs: run: brew install expect - name: Build Roc from pinned commit - run: | - echo "Building roc from pinned commit..." - ROC_COMMIT=$(python3 ci/get_roc_commit.py) - - git init roc-src - cd roc-src - git remote add origin https://github.com/roc-lang/roc - git fetch --depth 1 origin "$ROC_COMMIT" - git checkout --detach "$ROC_COMMIT" - - zig build roc - - echo "$(pwd)/zig-out/bin" >> "$GITHUB_PATH" + run: ./ci/build_pinned_roc.sh - name: Download bundle artifact uses: actions/download-artifact@v4 @@ -179,7 +155,7 @@ jobs: uses: roc-lang/roc/.github/actions/flaky-retry@main with: command: bash ci/all_tests.sh - error_string_contains: "error: unable" + error_string_contains: "error: (unable|invalid HTTP response)|HttpConnectionClosing" env: # Skip native build and rebundling since examples already point at the # downloaded bundle served by the previous step. diff --git a/.roc-version b/.roc-version index 771c05fa..963c5290 100644 --- a/.roc-version +++ b/.roc-version @@ -1 +1 @@ -34fe463f45e994b33621578298d1112a764a245b +1fc827d1e5867221c344bcd3f57a72e1929bae75 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 81f26c24..5a03138a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ The pinned Roc compiler commit lives in `.roc-version`. To update it: 1. Update `.roc-version` to the full 40-character Roc commit SHA. 2. Run `./ci/regenerate_glue.sh` to refresh `src/roc_platform_abi.rs`. -3. Run `cargo check --workspace` and `./ci/all_tests.sh` to verify the new compiler works. +3. Run `cargo check` and `./ci/all_tests.sh` to verify the new compiler works. ## How to generate docs? @@ -27,7 +27,7 @@ When the platform API changes in `platform/*.roc`, regenerate the Rust ABI bindi The script writes `src/roc_platform_abi.rs` using Roc's `RustGlue.roc` generator. It defaults to a sibling `../roc` checkout, and you can override paths when needed: ```bash -ROC=/path/to/roc/zig-out/bin/roc ROC_SRC=/path/to/roc ./ci/regenerate_glue.sh +ROC=../roc/zig-out/bin/roc ROC_SRC=../roc ./ci/regenerate_glue.sh ``` To verify the checked-in glue is current without modifying the worktree: @@ -38,14 +38,4 @@ To verify the checked-in glue is current without modifying the worktree: Commit the platform API change, the regenerated `src/roc_platform_abi.rs`, and any required Rust host implementation updates together. Do not edit the generated glue file manually. -## Updating Roc - -The pinned Roc compiler commit lives in `.roc-version`. After changing it, run: - -```bash -./ci/regenerate_glue.sh -cargo check --workspace -./build.sh -``` - Run `./ci/all_tests.sh` before opening a release or CI-facing PR. diff --git a/ci/all_tests.sh b/ci/all_tests.sh index 3c0085d8..b26e902e 100755 --- a/ci/all_tests.sh +++ b/ci/all_tests.sh @@ -72,23 +72,7 @@ if [ "$NEED_BUILD" = true ] && [ -d "roc-src" ] && [ -f "roc-src/zig-out/bin/roc fi if [ "$NEED_BUILD" = true ]; then - echo "Building roc from pinned commit $ROC_COMMIT..." - - rm -rf roc-src - git init roc-src - cd roc-src - git remote add origin https://github.com/roc-lang/roc - git fetch --depth 1 origin "$ROC_COMMIT" - git checkout --detach "$ROC_COMMIT" - - zig build roc - - # Add to GITHUB_PATH if running in CI - if [ -n "${GITHUB_PATH:-}" ]; then - echo "$(pwd)/zig-out/bin" >> "$GITHUB_PATH" - fi - - cd .. + ROC_COMMIT="$ROC_COMMIT" ./ci/build_pinned_roc.sh USE_ROC_SRC=true fi @@ -134,11 +118,16 @@ MIGRATED_EXAMPLES=( "random" "locale" "tty" + "dir" + "env-var" ) EXAMPLES_DIR="${ROOT_DIR}/examples/" export EXAMPLES_DIR +TESTS_DIR="${ROOT_DIR}/tests/" +export TESTS_DIR + # Check if all target libraries exist for bundling ALL_TARGETS_EXIST=true for target in x64mac arm64mac x64musl arm64musl; do @@ -207,6 +196,18 @@ for example in "${MIGRATED_EXAMPLES[@]}"; do roc check "examples/${example}.roc" done +TESTS_FILES=() +for roc_file in "${TESTS_DIR}"*.roc; do + [ -f "$roc_file" ] && TESTS_FILES+=("$(basename "${roc_file%.roc}")") +done + +echo "" +echo "=== Checking tests ===" +for test in "${TESTS_FILES[@]}"; do + echo "Checking: ${test}.roc" + roc check "tests/${test}.roc" +done + # roc build migrated examples echo "" if [ "$USE_BUNDLE" = true ]; then diff --git a/ci/build_pinned_roc.sh b/ci/build_pinned_roc.sh new file mode 100755 index 00000000..e5ba113c --- /dev/null +++ b/ci/build_pinned_roc.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +ROC_COMMIT="${ROC_COMMIT:-$(python3 ci/get_roc_commit.py)}" +MAX_ATTEMPTS="${ROC_BUILD_RETRY_COUNT:-4}" +TRANSIENT_BUILD_ERROR='error: (unable|invalid HTTP response)|HttpConnectionClosing' + +echo "Building roc from pinned commit $ROC_COMMIT..." + +rm -rf roc-src +git init roc-src +cd roc-src +git remote add origin https://github.com/roc-lang/roc +git fetch --depth 1 origin "$ROC_COMMIT" +git checkout --detach "$ROC_COMMIT" + +attempt=1 +while [ "$attempt" -le "$MAX_ATTEMPTS" ]; do + echo "Building roc attempt $attempt of $MAX_ATTEMPTS" + output_file="$(mktemp)" + + set +e + zig build roc > "$output_file" 2>&1 + status=$? + set -e + + cat "$output_file" + + if [ "$status" -eq 0 ]; then + rm -f "$output_file" + break + fi + + if [ "$attempt" -ge "$MAX_ATTEMPTS" ]; then + rm -f "$output_file" + exit "$status" + fi + + if grep -Eq "$TRANSIENT_BUILD_ERROR" "$output_file"; then + echo "Transient roc build failure; retrying..." + rm -f "$output_file" + attempt=$((attempt + 1)) + sleep 2 + else + rm -f "$output_file" + exit "$status" + fi +done + +if [ -n "${GITHUB_PATH:-}" ]; then + echo "$(pwd)/zig-out/bin" >> "$GITHUB_PATH" +fi diff --git a/ci/expect_scripts/command.exp b/ci/expect_scripts/command.exp index 81a4616c..abeafdd3 100644 --- a/ci/expect_scripts/command.exp +++ b/ci/expect_scripts/command.exp @@ -9,18 +9,23 @@ source ./ci/expect_scripts/shared-code.exp spawn $env(EXAMPLES_DIR)command -expect "Hello" -expect "{stderr_utf8_lossy: \"\", stdout_utf8: \"Hi" -expect "\"}" -expect "BAZ=DUCK" -expect "FOO=BAR" -expect "XYZ=ABC" -expect "cat: non_existent.txt: No such file or directory" -expect "Exit code: 1" +set expected_output [normalize_output { +Hello +{ stderr_utf8_lossy: "", stdout_utf8: "Hi +" } +BAZ=DUCK +FOO=BAR +XYZ=ABC +cat: non_existent.txt: No such file or directory +Exit code: 1 +{ stderr_bytes: [], stdout_bytes: [72, 105, 10] } +}] -expect eof { - check_exit_and_segfault +expect -exact $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 \ No newline at end of file +exit 1 diff --git a/ci/expect_scripts/env-var.exp b/ci/expect_scripts/env-var.exp index 63ae3710..01c24c9f 100644 --- a/ci/expect_scripts/env-var.exp +++ b/ci/expect_scripts/env-var.exp @@ -8,14 +8,12 @@ set timeout 7 source ./ci/expect_scripts/shared-code.exp set env(EDITOR) nano -set env(LETTERS) a,c,e,j spawn $env(EXAMPLES_DIR)env-var set expected_output [normalize_output { Your favorite editor is nano! -Your favorite letters are: a c e j }] expect $expected_output { diff --git a/ci/regenerate_glue.sh b/ci/regenerate_glue.sh index 467e35ab..63c9812d 100755 --- a/ci/regenerate_glue.sh +++ b/ci/regenerate_glue.sh @@ -2,9 +2,11 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + ROC_BIN="${ROC:-roc}" -PLATFORM_FILE="${PLATFORM_FILE:-$ROOT_DIR/platform/main.roc}" -GLUE_OUT_DIR="${GLUE_OUT_DIR:-$ROOT_DIR/src}" +PLATFORM_FILE="${PLATFORM_FILE:-platform/main.roc}" +GLUE_OUT_DIR="${GLUE_OUT_DIR:-src}" MODE="write" usage() { @@ -66,8 +68,8 @@ find_glue_spec() { fi candidates+=( - "$ROOT_DIR/../roc/src/glue/src/RustGlue.roc" - "$ROOT_DIR/../../roc/src/glue/src/RustGlue.roc" + "../roc/src/glue/src/RustGlue.roc" + "../../roc/src/glue/src/RustGlue.roc" ) for candidate in "${candidates[@]}"; do diff --git a/examples/bytes-stdin-stdout.roc b/examples/bytes-stdin-stdout.roc index 11ca1a3d..98abca49 100644 --- a/examples/bytes-stdin-stdout.roc +++ b/examples/bytes-stdin-stdout.roc @@ -3,13 +3,12 @@ app [main!] { pf: platform "../platform/main.roc" } import pf.Stdin import pf.Stdout import pf.Stderr -import pf.Arg exposing [Arg] # To run this example: check the README.md in this folder -main! : List Arg => Result {} _ -main! = |_args| +main! = |_args| { data = Stdin.bytes!({})? Stderr.write_bytes!(data)? Stdout.write_bytes!(data)? - Ok {} + Ok({}) +} \ No newline at end of file diff --git a/examples/command-line-args.roc b/examples/command-line-args.roc index 3f92fee2..f557ec5a 100644 --- a/examples/command-line-args.roc +++ b/examples/command-line-args.roc @@ -2,16 +2,15 @@ app [main!] { pf: platform "../platform/main.roc" } import pf.Stdout -main! : List(Str) => Try({}, [Exit(I32)]) main! = |args| { # Skip first arg (executable path), get the remaining args match args.drop_first(1) { [first_arg, ..] => { - Stdout.line!("received argument: ${first_arg}") + _ = Stdout.line!("received argument: ${first_arg}") Ok({}) } [] => { - Stdout.line!("Error: I expected one argument, but got none.") + _ = Stdout.line!("Error: I expected one argument, but got none.") Err(Exit(1)) } } diff --git a/examples/command.roc b/examples/command.roc index 74a8a94c..b082d64e 100644 --- a/examples/command.roc +++ b/examples/command.roc @@ -6,45 +6,42 @@ import pf.Cmd # Different ways to run commands like you do in a terminal. main! = |_args| { - # Simplest way to execute a command (prints to your terminal). - exec_result = Cmd.exec!("echo", ["Hello"]) - match exec_result { - Ok({}) => {} - Err(_) => Stdout.line!("Error running echo") - } - - # To execute and capture the output (stdout and stderr) without inheriting your terminal. - output_result = Cmd.exec_output!(Cmd.args(Cmd.new("echo"), ["Hi"])) - match output_result { - Ok(cmd_output) => Stdout.line!("{stderr_utf8_lossy: \"${cmd_output.stderr_utf8_lossy}\", stdout_utf8: \"${cmd_output.stdout_utf8}\"}") - Err(_) => Stdout.line!("Error capturing output") - } - - # To run a command with environment variables. - env_cmd = Cmd.args( - Cmd.envs( - Cmd.env( - Cmd.clear_envs(Cmd.new("env")), - "FOO", - "BAR", - ), - [("BAZ", "DUCK"), ("XYZ", "ABC")], - ), - ["-v"], - ) - env_result = Cmd.exec_cmd!(env_cmd) - match env_result { - Ok({}) => {} - Err(_) => Stdout.line!("Error running env") - } - - # To execute and just get the exit code (prints to your terminal). - # Prefer using `exec!` or `exec_cmd!`. - exit_result = Cmd.exec_exit_code!(Cmd.args(Cmd.new("cat"), ["non_existent.txt"])) - match exit_result { - Ok(exit_code) => Stdout.line!("Exit code: ${exit_code.to_str()}") - Err(_) => Stdout.line!("Error getting exit code") - } - - Ok({}) + # Simplest way to execute a command (prints to your terminal). + Cmd.exec!("echo", ["Hello"])? + + # To execute and capture the output (stdout and stderr) without inheriting your terminal. + cmd_output = + Cmd.new("echo") + .args(["Hi"]) + .exec_output!()? + + _ = Stdout.line!("${Str.inspect(cmd_output)}") + + # To run a command with environment variables. + Cmd.new("env") + .clear_envs() # You probably don't need to clear all other environment variables, this is just an example. + .env("BAZ", "DUCK") + .env("FOO", "BAR") + .env("XYZ", "ABC") + .exec_cmd!()? + + # To execute and just get the exit code (prints to your terminal). + # Prefer using `exec!` or `exec_cmd!`. + exit_code = + Cmd.new("cat") + .args(["non_existent.txt"]) + .exec_exit_code!()? + + _ = Stdout.line!("Exit code: ${exit_code.to_str()}") + + # 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") + .args(["Hi"]) + .exec_output_bytes!()? + + _ = Stdout.line!("${Str.inspect(cmd_output_bytes)}") + + Ok({}) } diff --git a/examples/dir.roc b/examples/dir.roc index 249e1156..aba8ce26 100644 --- a/examples/dir.roc +++ b/examples/dir.roc @@ -1,35 +1,46 @@ app [main!] { pf: platform "../platform/main.roc" } import pf.Stdout +import pf.Stderr import pf.Dir # Demo of all Dir functions. main! = |_args| { - # Create a directory - Dir.create!("empty-dir")? + dir_result = || { + # Create a directory + Dir.create!("empty-dir")? - # Create a directory and its parents - Dir.create_all!("nested-dir/a/b/c")? + # Create a directory and its parents + Dir.create_all!("nested-dir/a/b/c")? - # Create a child directory - Dir.create!("nested-dir/child")? + # Create a child directory + Dir.create!("nested-dir/child")? - # List the contents of a directory - paths = Dir.list!("nested-dir")? + # List the contents of a directory + paths = Dir.list!("nested-dir")? - # Check the contents of the directory - expect List.len(paths) == 2 - expect List.contains(paths, "nested-dir/a") - expect List.contains(paths, "nested-dir/child") + # Check the contents of the directory + expect List.len(paths) == 2 + expect List.contains(paths, "nested-dir/a") + expect List.contains(paths, "nested-dir/child") - # Delete an empty directory - Dir.delete_empty!("empty-dir")? + # Delete an empty directory + Dir.delete_empty!("empty-dir")? - # Delete all directories recursively - Dir.delete_all!("nested-dir")? + # Delete all directories recursively + Dir.delete_all!("nested-dir")? - Stdout.line!("Success!") + _ = Stdout.line!("Success!") - Ok({}) + Ok({}) + } + + match dir_result() { + Ok(_) => Ok({}), + Err(err) => { + _ = Stderr.line!("Error during directory operations: ${Str.inspect(err)}") + Err(Exit(1)) + } + } } diff --git a/examples/env-var.roc b/examples/env-var.roc index d56c8c1a..f703b2c4 100644 --- a/examples/env-var.roc +++ b/examples/env-var.roc @@ -1,19 +1,23 @@ app [main!] { pf: platform "../platform/main.roc" } import pf.Stdout +import pf.Stderr import pf.Env # How to read environment variables with Env.var! -main! : List(Str) => Try({}, [Exit(I32)]) main! = |_args| { - editor = Env.var!("EDITOR") + result = Env.var!("EDITOR") - if Str.is_empty(editor) { - Stdout.line!("EDITOR is not set") - } else { - Stdout.line!("Your favorite editor is ${editor}!") - } + match result { + Ok(editor) => { + _ = Stdout.line!("Your favorite editor is ${editor}!") + Ok({}) + } - Ok({}) + Err(VarNotFound(name)) => { + _ = Stderr.line!("Env var ${name} is not set.") + Ok({}) + } + } } diff --git a/examples/error-handling.roc b/examples/error-handling.roc index 51eaa0cd..7e9b56f4 100644 --- a/examples/error-handling.roc +++ b/examples/error-handling.roc @@ -11,22 +11,41 @@ main! = |_args| { # Try to read a file that doesn't exist - should error result = File.read_utf8!("nonexistent-file.txt") match result { - Ok(content) => Stdout.line!("Unexpected success: ${content}") - Err(FileErr(NotFound)) => Stdout.line!("Expected error: File not found (NotFound)") - Err(FileErr(PermissionDenied)) => Stdout.line!("Error: Permission denied") - Err(FileErr(Other(msg))) => Stdout.line!("Error: ${msg}") - Err(_) => Stdout.line!("Error: Other file error") + Ok(content) => { + _r = Stdout.line!("Unexpected success: ${content}") + } + Err(FileErr(NotFound)) => { + _r = Stdout.line!("Expected error: File not found (NotFound)") + } + Err(FileErr(PermissionDenied)) => { + _r = Stdout.line!("Error: Permission denied") + } + Err(FileErr(Other(msg))) => { + _r = Stdout.line!("Error: ${msg}") + } + Err(_) => { + _r = Stdout.line!("Error: Other file error") + } } # Now demonstrate success path - create, read, then cleanup - # Using ? operator to propagate errors (works with open tag unions) - File.write_utf8!(file_name, "Hello from error-handling example!")? + file_result = { + File.write_utf8!(file_name, "Hello from error-handling example!")? - content = File.read_utf8!(file_name)? - Stdout.line!("${file_name} contains: ${content}") + content = File.read_utf8!(file_name)? + _r = Stdout.line!("${file_name} contains: ${content}") - # Cleanup - File.delete!(file_name)? + # Cleanup + File.delete!(file_name)? - Ok({}) + Ok({}) + } + + match file_result { + Ok({}) => Ok({}) + Err(_) => { + _r = Stdout.line!("Error during file operations") + Err(Exit(1)) + } + } } diff --git a/examples/file-read-write.roc b/examples/file-read-write.roc index 49370325..24351536 100644 --- a/examples/file-read-write.roc +++ b/examples/file-read-write.roc @@ -5,29 +5,29 @@ import pf.File # Demo of File.read_utf8! and File.write_utf8! -main! : List(Str) => Try({}, [Exit(I32), FileErr(File.IOErr), ..]) main! = |_args| { out_file = "out.txt" - Stdout.line!("Writing a string to out.txt") + _ = Stdout.line!("Writing a string to out.txt") - match File.write_utf8!(out_file, "a string!") { - Ok({}) => {} - Err(FileErr(err)) => return Err(FileErr(err)) - } + file_result = || { + File.write_utf8!(out_file, "a string!")? - contents = match File.read_utf8!(out_file) { - Ok(value) => value - Err(FileErr(err)) => return Err(FileErr(err)) - } + contents = File.read_utf8!(out_file)? - Stdout.line!("I read the file back. Its contents are: \"${contents}\"") + _ = Stdout.line!("I read the file back. Its contents are: \"${contents}\"") - # Cleanup - match File.delete!(out_file) { - Ok({}) => {} - Err(FileErr(err)) => return Err(FileErr(err)) + # Cleanup + File.delete!(out_file)? + + Ok({}) } - Ok({}) + match file_result() { + Ok({}) => Ok({}) + Err(_) => { + _ = Stdout.line!("Error during file operations") + Err(Exit(1)) + } + } } diff --git a/examples/hello-world.roc b/examples/hello-world.roc index 3c4c1d42..9734e890 100644 --- a/examples/hello-world.roc +++ b/examples/hello-world.roc @@ -2,8 +2,7 @@ app [main!] { pf: platform "../platform/main.roc" } import pf.Stdout -main! : List(Str) => Try({}, [Exit(I32)]) main! = |_args| { - Stdout.line!("Hello, World!") + _r = Stdout.line!("Hello, World!") Ok({}) } diff --git a/examples/hello.roc b/examples/hello.roc index 16ee4281..c335e37d 100644 --- a/examples/hello.roc +++ b/examples/hello.roc @@ -4,6 +4,6 @@ import pf.Stdout main! : List(Str) => Try({}, [Exit(I32)]) main! = |_args| { - Stdout.line!("Hello from basic-cli!") + _ = Stdout.line!("Hello from basic-cli!") Ok({}) } diff --git a/examples/locale.roc b/examples/locale.roc index 541ea628..98337d65 100644 --- a/examples/locale.roc +++ b/examples/locale.roc @@ -6,12 +6,15 @@ import pf.Locale # Getting the preferred locale and all available locales main! = |_args| { - locale_str = Locale.get!() - Stdout.line!("The most preferred locale for this system or application: ${locale_str}") + locale_str = match Locale.get!() { + Ok(locale) => locale + Err(NotAvailable) => "" + } + match Stdout.line!("The most preferred locale for this system or application: ${locale_str}") { _ => {} } all_locales = Locale.all!() locales_str = Str.join_with(all_locales, ", ") - Stdout.line!("All available locales for this system or application: [${locales_str}]") + match Stdout.line!("All available locales for this system or application: [${locales_str}]") { _ => {} } Ok({}) } diff --git a/examples/path.roc b/examples/path.roc index 47cec5dd..34abf856 100644 --- a/examples/path.roc +++ b/examples/path.roc @@ -5,18 +5,14 @@ import pf.Path # Demo of basic-cli Path functions -main! : List(Str) => Try({}, [Exit(I32)]) main! = |_args| { - path = "path.roc" + path = Path.from_str("path.roc") - a = Path.is_file!(path) - b = Path.is_dir!(path) - c = Path.is_sym_link!(path) - - Stdout.line!( - \\is_file: ${Str.inspect(a)} - \\is_dir: ${Str.inspect(b)} - \\is_sym_link: ${Str.inspect(c)} + _ = Stdout.line!( + \\is_file: ${Str.inspect(Path.is_file!(path))} + \\is_dir: ${Str.inspect(Path.is_dir!(path))} + \\is_sym_link: ${Str.inspect(Path.is_sym_link!(path))} + \\display: ${Path.display(path)} ) Ok({}) diff --git a/examples/print.roc b/examples/print.roc index b0383227..1cfe5f53 100644 --- a/examples/print.roc +++ b/examples/print.roc @@ -5,23 +5,22 @@ import pf.Stderr # Printing to stdout and stderr -main! : List(Str) => Try({}, [Exit(I32)]) main! = |_args| { # Print a string to stdout - Stdout.line!("Hello, world!") + match Stdout.line!("Hello, world!") { _ => {} } # Print without a newline - Stdout.write!("No newline after me.") + match Stdout.write!("No newline after me.") { _ => {} } # Print a string to stderr - Stderr.line!("Hello, error!") + match Stderr.line!("Hello, error!") { _ => {} } # Print a string to stderr without a newline - Stderr.write!("Err with no newline after.") + match Stderr.write!("Err with no newline after.") { _ => {} } # Print a list to stdout List.for_each!(["Foo", "Bar", "Baz"], |str| { - Stdout.line!(str) + match Stdout.line!(str) { _ => {} } }) Ok({}) diff --git a/examples/random.roc b/examples/random.roc index 7b0871d5..f4883b2b 100644 --- a/examples/random.roc +++ b/examples/random.roc @@ -9,11 +9,11 @@ main! = |_args| { result = Random.seed_u64!({}) match result { Ok(random_u64) => { - Stdout.line!("Random U64 seed is: ${random_u64.to_str()}") + _r = Stdout.line!("Random U64 seed is: ${random_u64.to_str()}") Ok({}) } Err(_) => { - Stdout.line!("Failed to generate random seed") + _r = Stdout.line!("Failed to generate random seed") Err(Exit(1)) } } diff --git a/examples/stdin-basic.roc b/examples/stdin-basic.roc index facc7d3f..1544abf9 100644 --- a/examples/stdin-basic.roc +++ b/examples/stdin-basic.roc @@ -3,14 +3,19 @@ app [main!] { pf: platform "../platform/main.roc" } import pf.Stdin import pf.Stdout -main! : List(Str) => Try({}, [Exit(I32)]) main! = |_args| { - Stdout.line!("What's your first name?") - first = Stdin.line!({}) + match Stdout.line!("What's your first name?") { _ => {} } + first = match Stdin.line!({}) { + Ok(line) => line + Err(_) => "" + } - Stdout.line!("What's your last name?") - last = Stdin.line!({}) + match Stdout.line!("What's your last name?") { _ => {} } + last = match Stdin.line!({}) { + Ok(line) => line + Err(_) => "" + } - Stdout.line!("Hi, ${first} ${last}! \u(1F44B)") + match Stdout.line!("Hi, ${first} ${last}! \u(1F44B)") { _ => {} } Ok({}) } diff --git a/examples/time.roc b/examples/time.roc index 403faa20..ea8adcc2 100644 --- a/examples/time.roc +++ b/examples/time.roc @@ -6,7 +6,6 @@ import pf.Sleep # Demo Utc and Sleep functions -main! : List(Str) => Try({}, [Exit(I32)]) main! = |_args| { start = Utc.now!({}) @@ -15,10 +14,10 @@ main! = |_args| { finish = Utc.now!({}) - duration_nanos = finish - start - duration_ms = duration_nanos // 1_000_000 + duration_ms = Utc.delta_as_millis(finish, start) + duration_nanos = Utc.delta_as_nanos(finish, start) - Stdout.line!("Completed in ${duration_ms.to_str()} ms (${duration_nanos.to_str()} ns)") + _r = Stdout.line!("Completed in ${duration_ms.to_str()} ms (${duration_nanos.to_str()} ns)") Ok({}) } diff --git a/examples/tty.roc b/examples/tty.roc index 5e18476b..8c30b1b9 100644 --- a/examples/tty.roc +++ b/examples/tty.roc @@ -7,10 +7,10 @@ import pf.Tty ## This is useful for running an app like vim or a game in the terminal. main! = |_args| { - Stdout.line!("Tty: enabling raw mode") + match Stdout.line!("Tty: enabling raw mode") { _ => {} } Tty.enable_raw_mode!() - Stdout.line!("Tty: disabling raw mode") + match Stdout.line!("Tty: disabling raw mode") { _ => {} } Tty.disable_raw_mode!() Ok({}) diff --git a/platform/Cmd.roc b/platform/Cmd.roc index 299aae91..6ecaecf1 100644 --- a/platform/Cmd.roc +++ b/platform/Cmd.roc @@ -1,12 +1,173 @@ +import IOErr exposing [IOErr] + Cmd :: { args : List(Str), clear_envs : Bool, - envs : List(Str), + envs : List(Str), # TODO change this to List((Str, Str)) program : Str, }.{ - IOErr := [NotFound, PermissionDenied, BrokenPipe, AlreadyExists, Interrupted, Unsupported, OutOfMemory, Other(Str)] + host_exec_exit_code! : Cmd => Try(I32, IOErr) + + host_exec_output! : Cmd => Try(OutputFromHostSuccess, Try(OutputFromHostFailure, IOErr)) + + + ## Simplest way to execute a command by name with arguments. + ## Stdin, stdout, and stderr are inherited from the parent process. + ## + ## If you want to capture the output, use [exec_output!] instead. + ## + ## ```roc + ## Cmd.exec!("echo", ["hello world"])? + ## ``` + exec! : Str, List(Str) => Try({}, [ExecFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), ..]) + exec! = |program, arguments| { + exit_code = + new(program) + .args(arguments) + .exec_exit_code!()? + + if exit_code == 0 { + Ok({}) + } else { + command = "${program} ${Str.join_with(arguments, " ")}" + Err(ExecFailed({ command, exit_code })) + } + } + + ## Execute a Cmd (using the builder pattern). + ## Stdin, stdout, and stderr are inherited from the parent process. + ## + ## You should prefer using [exec!] instead, only use this if you want to use [env], [envs] or [clear_envs]. + ## If you want to capture the output, use [exec_output!] instead. + ## + ## ```roc + ## Cmd.new("cargo") + ## .arg(["build") + ## .env("RUST_BACKTRACE", "1") + ## .exec_cmd!()? + ## ``` + exec_cmd! : Cmd => Try({}, [ExecCmdFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), ..]) + exec_cmd! = |cmd| { + exit_code = exec_exit_code!(cmd)? + + if exit_code == 0 { + Ok({}) + } else { + Err(ExecCmdFailed({ command: to_str(cmd), exit_code })) + } + } + + ## Execute command and capture stdout and stderr as UTF-8 strings. + ## Invalid UTF-8 sequences are replaced with the Unicode replacement character. + ## + ## Use [exec_output_bytes!] instead if you want to capture the output in the original form as bytes. + ## [exec_output_bytes!] may also be used for maximum performance, because you may be able to avoid unnecessary UTF-8 conversions. + ## + ## ```roc + ## cmd_output = + ## Cmd.new("echo") + ## .args(["Hi"]) + ## .exec_output!()? + ## + ## Stdout.line!("Echo output: ${cmd_output.stdout_utf8}")? + ## ``` + exec_output! : Cmd => Try( + { stdout_utf8 : Str, stderr_utf8_lossy : Str }, + [ + StdoutContainsInvalidUtf8({ cmd_str : Str, err : [BadUtf8({ problem : _, index : U64 })] }), + NonZeroExitCode({ command : Str, exit_code : I32, stdout_utf8_lossy : Str, stderr_utf8_lossy : Str }), + FailedToGetExitCode({ command : Str, err : IOErr }), + .. + ] + ) + exec_output! = |cmd| { + exec_try = Cmd.host_exec_output!(cmd) + + match exec_try { + Ok({ stderr_bytes, stdout_bytes }) => { + stdout_utf8 = + Str.from_utf8(stdout_bytes) + .map_err(|err| StdoutContainsInvalidUtf8({ cmd_str: to_str(cmd), err }))? + + stderr_utf8_lossy = Str.from_utf8_lossy(stderr_bytes) - ## Create a new command with the given program name. + Ok({ stdout_utf8, stderr_utf8_lossy }) + } + Err(inside_try) => { + match inside_try { + Ok({ exit_code, stderr_bytes, stdout_bytes }) => { + stdout_utf8_lossy = Str.from_utf8_lossy(stdout_bytes) + stderr_utf8_lossy = Str.from_utf8_lossy(stderr_bytes) + + Err(NonZeroExitCode({ command: to_str(cmd), exit_code, stdout_utf8_lossy, stderr_utf8_lossy })) + } + Err(err) => { + Err(FailedToGetExitCode({ command: to_str(cmd), err })) + } + } + } + } + } + + ## Execute command and capture stdout and stderr in the original form as bytes. + ## + ## Use [exec_output!] instead if you want to get the output as UTF-8 strings. + ## + ## ```roc + ## cmd_output = + ## Cmd.new("echo") + ## .args(["Hi"]) + ## .exec_output_bytes!()? + ## + ## Stdout.line!("${Str.inspect(cmd_output_bytes)}")? # {stderr_bytes: [], stdout_bytes: [72, 105, 10]} + ## ``` + exec_output_bytes! : Cmd => Try( + { stderr_bytes : List(U8), stdout_bytes : List(U8) }, + [ + NonZeroExitCodeB({ exit_code : I32, stdout_bytes : List(U8), stderr_bytes : List(U8) }), + FailedToGetExitCodeB(IOErr), + .. + ] + ) + exec_output_bytes! = |cmd| { + exec_try = Cmd.host_exec_output!(cmd) + + match exec_try { + Ok({ stderr_bytes, stdout_bytes }) => + Ok({ stdout_bytes, stderr_bytes }) + + Err(inside_try) => + match inside_try { + Ok({ exit_code, stderr_bytes, stdout_bytes }) => { + Err(NonZeroExitCodeB({ exit_code, stdout_bytes, stderr_bytes })) + } + + Err(err) => { + Err(FailedToGetExitCodeB(err)) + } + } + } + } + + ## Execute a command and return its exit code. + ## Stdin, stdout, and stderr are inherited from the parent process. + ## + ## You should prefer using [exec!] or [exec_cmd!] instead, only use this if you want to take a specific action based on a **specific non-zero exit code**. + ## For example, `roc check` returns exit code 1 if there are errors, and exit code 2 if there are only warnings. + ## So, you could use `exec_exit_code!` to ignore warnings on `roc check`. + ## + ## ```roc + ## exit_code = Cmd.new("cat").arg("non_existent.txt").exec_exit_code!()? + ## ``` + exec_exit_code! : Cmd => Try(I32, [FailedToGetExitCode({ command : Str, err : IOErr }), ..]) + exec_exit_code! = |cmd| { + match Cmd.host_exec_exit_code!(cmd) { + Ok(num) => Ok(num) + Err(io_err) => Err(FailedToGetExitCode({ command : to_str(cmd), err: io_err })) + } + } + + ## Create a new command with the given program name. Use a function that starts with `exec_` to execute it. ## ## ```roc ## cmd = Cmd.new("ls") @@ -20,42 +181,39 @@ Cmd :: { } ## Add a single argument to the command. + ## ❗ Shell features like variable subsitition (e.g. `$FOO`), glob patterns (e.g. `*.txt`), ... are not available. ## ## ```roc ## cmd = Cmd.new("ls").arg("-l") ## ``` arg : Cmd, Str -> Cmd arg = |cmd, a| { - args: List.append(cmd.args, a), - clear_envs: cmd.clear_envs, - envs: cmd.envs, - program: cmd.program, + ..cmd, + args: cmd.args.append(a), } ## Add multiple arguments to the command. + ## ❗ Shell features like variable subsitition (e.g. `$FOO`), glob patterns (e.g. `*.txt`), ... are not available. ## ## ```roc ## cmd = Cmd.new("ls").args(["-l", "-a"]) ## ``` args : Cmd, List(Str) -> Cmd args = |cmd, new_args| { - args: List.concat(cmd.args, new_args), - clear_envs: cmd.clear_envs, - envs: cmd.envs, - program: cmd.program, + ..cmd, + args: cmd.args.concat(new_args), } ## Add a single environment variable to the command. ## + ## ## ```roc - ## cmd = Cmd.new("env").env("FOO", "bar") + ## cmd = Cmd.new("env").env("FOO", "bar") # add the environment variable "FOO" with value "bar" ## ``` env : Cmd, Str, Str -> Cmd env = |cmd, key, value| { - args: cmd.args, - clear_envs: cmd.clear_envs, - envs: List.concat(cmd.envs, [key, value]), - program: cmd.program, + new_envs = cmd.envs.append(key).append(value) + { args: cmd.args, clear_envs: cmd.clear_envs, envs: new_envs, program: cmd.program } } ## Add multiple environment variables to the command. @@ -65,20 +223,19 @@ Cmd :: { ## ``` envs : Cmd, List((Str, Str)) -> Cmd envs = |cmd, pairs| { - flat = List.fold(pairs, [], |acc, (k, v)| List.concat(acc, [k, v])) - { - args: cmd.args, - clear_envs: cmd.clear_envs, - envs: List.concat(cmd.envs, flat), - program: cmd.program, - } + new_envs = flatten_str_pairs(pairs, cmd.envs, 0) + { args: cmd.args, clear_envs: cmd.clear_envs, envs: new_envs, program: cmd.program } } ## Clear all environment variables before running the command. ## Only environment variables added via `env` or `envs` will be available. + ## Useful if you want a clean command run that does not behave unexpectedly if the user has some env var set. ## ## ```roc - ## cmd = Cmd.new("env").clear_envs().env("ONLY_THIS", "visible") + ## cmd = + ## Cmd.new("env") + ## .clear_envs() + ## .env("ONLY_THIS", "visible") ## ``` clear_envs : Cmd -> Cmd clear_envs = |cmd| { @@ -87,63 +244,50 @@ Cmd :: { envs: cmd.envs, program: cmd.program, } +} - ## Execute a command and return its exit code. - ## Stdin, stdout, and stderr are inherited from the parent process. - ## - ## ```roc - ## exit_code = Cmd.new("ls").arg("-l").exec_exit_code!()? - ## ``` - exec_exit_code! : Cmd => Try(I32, [CmdErr(IOErr)]) +# Do not change the order of the fields! It will lead to a segfault. +OutputFromHostSuccess : { + stderr_bytes : List(U8), + stdout_bytes : List(U8), +} - ## Execute command and capture stdout/stderr as UTF-8 strings. - ## Invalid UTF-8 sequences are replaced with the Unicode replacement character. - ## - ## ```roc - ## cmd_output = - ## Cmd.new("echo") - ## .args(["Hi"]) - ## .exec_output!()? - ## - ## Stdout.line!("Echo output: ${cmd_output.stdout_utf8}")? - ## ``` - exec_output! : Cmd => Try( - { stdout_utf8 : Str, stderr_utf8_lossy : Str }, - [CmdErr(IOErr), NonZeroExit({ exit_code : I32, stdout_utf8_lossy : Str, stderr_utf8_lossy : Str })] - ) +# Do not change the order of the fields! It will lead to a segfault. +OutputFromHostFailure : { + stderr_bytes : List(U8), + stdout_bytes : List(U8), + exit_code : I32, +} - ## Simple helper to execute a command by name with arguments. - ## Stdin, stdout, and stderr are inherited from the parent process. - ## Returns Ok if the command exits with code 0. - ## - ## ```roc - ## Cmd.exec!("ls", ["-l", "-a"])? - ## ``` - exec! : Str, List(Str) => Try({}, [CmdErr(IOErr), ExecFailed({ command : Str, exit_code : I32 })]) - exec! = |program, arguments| { - cmd = new(program).args(arguments) - result = Cmd.exec_exit_code!(cmd) - match result { - Ok(0) => Ok({}), - Ok(exit_code) => Err(ExecFailed({ command: program, exit_code })), - Err(CmdErr(io_err)) => Err(CmdErr(io_err)), +flatten_str_pairs : List((Str, Str)), List(Str), U64 -> List(Str) +flatten_str_pairs = |pairs, acc, idx| { + if idx >= pairs.len() { + acc + } else { + match pairs.get(idx) { + Ok(pair) => + flatten_str_pairs(pairs, acc.append(pair.0).append(pair.1), idx + 1) + Err(_) => + acc } } +} - ## Execute a command using the builder pattern. - ## Stdin, stdout, and stderr are inherited from the parent process. - ## Returns Ok if the command exits with code 0. - ## - ## ```roc - ## Cmd.new("ls").args(["-l", "-a"]).exec_cmd!()? - ## ``` - exec_cmd! : Cmd => Try({}, [CmdErr(IOErr), ExecFailed({ exit_code : I32 })]) - exec_cmd! = |cmd| { - result = Cmd.exec_exit_code!(cmd) - match result { - Ok(0) => Ok({}), - Ok(code) => Err(ExecFailed({ exit_code: code })), - Err(CmdErr(io_err)) => Err(CmdErr(io_err)), +to_str : Cmd -> Str +to_str = |cmd| { + my_trim = |trimmed_str| + if Str.is_empty(trimmed_str) { + "" + } else { + "envs: ${trimmed_str}" } - } + + envs_str = + # TODO once we're using List of tuples: .map(|(key, value)| "${key}=${value}") + my_trim(Str.trim(Str.join_with(cmd.envs, " "))) + + clear_envs_str = if cmd.clear_envs { ", clear_envs: true" } else { "" } + args_str = Str.join_with(cmd.args, " ") + + "{ cmd: ${cmd.program}, args: ${args_str}${envs_str}${clear_envs_str} }" } diff --git a/platform/Dir.roc b/platform/Dir.roc index f5863674..d8817c89 100644 --- a/platform/Dir.roc +++ b/platform/Dir.roc @@ -1,24 +1,6 @@ -Dir := [].{ - ## **NotFound** - An entity was not found, often a file. - ## - ## **PermissionDenied** - The operation lacked the necessary privileges to complete. - ## - ## **AlreadyExists** - An entity already exists, often a file. - ## - ## **NotADirectory** - The path was not a directory when a directory was expected. - ## - ## **NotEmpty** - The directory is not empty. - ## - ## **Other** - A custom error that does not fall under any other I/O error kind. - IOErr := [ - NotFound, - PermissionDenied, - AlreadyExists, - NotADirectory, - NotEmpty, - Other(Str), - ] +import IOErr exposing [IOErr] +Dir := [].{ ## Creates a new, empty directory at the provided path. ## ## If the parent directories do not exist, they will not be created. diff --git a/platform/Env.roc b/platform/Env.roc index 2047f681..7949c744 100644 --- a/platform/Env.roc +++ b/platform/Env.roc @@ -4,17 +4,17 @@ Env := [].{ ## If the value is invalid Unicode, the invalid parts will be replaced with the ## [Unicode replacement character](https://unicode.org/glossary/#replacement_character). ## - ## Returns an empty string if the variable is not found. - var! : Str => Str + ## Returns `Err(VarNotFound(name))` if the variable is not set. + var! : Str => Try(Str, [VarNotFound(Str)]) ## Reads the [current working directory](https://en.wikipedia.org/wiki/Working_directory) ## from the environment. ## - ## Returns an empty string if the cwd is unavailable. - cwd! : {} => Str + ## Returns `Err(CwdUnavailable)` if the cwd cannot be determined. + cwd! : {} => Try(Str, [CwdUnavailable]) ## Gets the path to the currently-running executable. ## - ## Returns an empty string if the path is unavailable. - exe_path! : {} => Str + ## Returns `Err(ExePathUnavailable)` if the path cannot be determined. + exe_path! : {} => Try(Str, [ExePathUnavailable]) } diff --git a/platform/File.roc b/platform/File.roc index 9e2c0749..e02cfece 100644 --- a/platform/File.roc +++ b/platform/File.roc @@ -1,30 +1,6 @@ -File := [].{ - ## **NotFound** - An entity was not found, often a file. - ## - ## **PermissionDenied** - The operation lacked the necessary privileges to complete. - ## - ## **BrokenPipe** - The operation failed because a pipe was closed. - ## - ## **AlreadyExists** - An entity already exists, often a file. - ## - ## **Interrupted** - This operation was interrupted. Interrupted operations can typically be retried. - ## - ## **Unsupported** - This operation is unsupported on this platform. This means that the operation can never succeed. - ## - ## **OutOfMemory** - An operation could not be completed, because it failed to allocate enough memory. - ## - ## **Other** - A custom error that does not fall under any other I/O error kind. - IOErr := [ - NotFound, - PermissionDenied, - BrokenPipe, - AlreadyExists, - Interrupted, - Unsupported, - OutOfMemory, - Other(Str), - ] +import IOErr exposing [IOErr] +File := [].{ ## Read all bytes from a file. read_bytes! : Str => Try(List(U8), [FileErr(IOErr)]) diff --git a/platform/IOErr.roc b/platform/IOErr.roc new file mode 100644 index 00000000..68e33f14 --- /dev/null +++ b/platform/IOErr.roc @@ -0,0 +1,27 @@ +## Represents an I/O error that can occur during platform operations. +## +## **NotFound** - An entity was not found, often a file. +## +## **PermissionDenied** - The operation lacked the necessary privileges to complete. +## +## **BrokenPipe** - The operation failed because a pipe was closed. +## +## **AlreadyExists** - An entity already exists, often a file. +## +## **Interrupted** - This operation was interrupted. Interrupted operations can typically be retried. +## +## **Unsupported** - This operation is unsupported on this platform. This means that the operation can never succeed. +## +## **OutOfMemory** - An operation could not be completed, because it failed to allocate enough memory. +## +## **Other** - A custom error that does not fall under any other I/O error kind. +IOErr := [ + AlreadyExists, + BrokenPipe, + Interrupted, + NotFound, + Other(Str), + OutOfMemory, + PermissionDenied, + Unsupported, +] diff --git a/platform/Locale.roc b/platform/Locale.roc index 6598902c..2d7ced38 100644 --- a/platform/Locale.roc +++ b/platform/Locale.roc @@ -2,7 +2,9 @@ Locale := [].{ ## Returns the most preferred locale for the system or application. ## ## The returned [Str] is a BCP 47 language tag, like `en-US` or `fr-CA`. - get! : () => Str + ## + ## Returns `Err(NotAvailable)` if the locale cannot be determined. + get! : () => Try(Str, [NotAvailable]) ## Returns the preferred locales for the system or application. ## diff --git a/platform/Path.roc b/platform/Path.roc index 8c8ba084..98aadbc3 100644 --- a/platform/Path.roc +++ b/platform/Path.roc @@ -1,45 +1,170 @@ -Path := [].{ - ## **NotFound** - An entity was not found, often a file. +import IOErr exposing [IOErr] + +PathType : { + is_file : Bool, + is_sym_link : Bool, + is_dir : Bool, +} + +Path :: [ + # We have these different internal representations for two reasons: + # 1. If I'm calling an OS API, passing a path I got from the OS is definitely safe. + # However, passing a Path I got from a RocStr might be unsafe; it may contain \0 + # characters, which would result in the operation happening on a totally different + # path. As such, we need to check for \0s and fail without calling the OS API if we + # find one in the path. + # 2. If I'm converting the Path to a Str, doing that conversion on a Path that was + # created from a RocStr needs no further processing. However, if it came from the OS, + # then we need to know what charset to assume it had, in order to decode it properly. + # These come from the OS (e.g. when reading a directory, calling `canonicalize`, + # or reading an environment variable - which, incidentally, are nul-terminated), + # so we know they are both nul-terminated and do not contain interior nuls. + # As such, they can be passed directly to OS APIs. + # + # Note that the nul terminator byte is right after the end of the length (into the + # unused capacity), so this can both be compared directly to other `List U8`s that + # aren't nul-terminated, while also being able to be passed directly to OS APIs. + FromOperatingSystem(List(U8)), + + # These come from userspace (e.g. Path.from_bytes), so they need to be checked for interior + # nuls and then nul-terminated before the host can pass them to OS APIs. + ArbitraryBytes(List(U8)), + + # This was created as a RocStr, so it might have interior nul bytes but it's definitely UTF-8. + # That means we can `to_str` it trivially, but have to validate before sending it to OS + # APIs that expect a nul-terminated `char*`. + # + # Note that both UNIX and Windows APIs will accept UTF-8, because on Windows the host calls + # `_setmbcp(_MB_CP_UTF8);` to set the process's Code Page to UTF-8 before doing anything else. + # See https://docs.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page#-a-vs--w-apis + # and https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setmbcp?view=msvc-170 + # for more details on the UTF-8 Code Page in Windows. + FromStr(Str), +].{ + host_path_type! : List(U8) => Try(PathType, IOErr) + + ## Returns `Bool.True` if the path exists on disk and is pointing at a regular file. ## - ## **PermissionDenied** - The operation lacked the necessary privileges to complete. + ## This function will traverse symbolic links to query information about the + ## destination file. In case of broken symbolic links this will return `Bool.False`. + is_file! : Path => Try(Bool, [PathErr(IOErr)]) + is_file! = |path| + match type!(path) { + Ok(IsFile) => Ok(Bool.True) + Ok(_) => Ok(Bool.False) + Err(err) => Err(err) + } + + ## Returns `Bool.True` if the path exists on disk and is pointing at a directory. ## - ## **BrokenPipe** - The operation failed because a pipe was closed. + ## This function will traverse symbolic links to query information about the + ## destination file. In case of broken symbolic links this will return `Bool.False`. + is_dir! : Path => Try(Bool, [PathErr(IOErr)]) + is_dir! = |path| + match type!(path) { + Ok(IsDir) => Ok(Bool.True) + Ok(_) => Ok(Bool.False) + Err(err) => Err(err) + } + + ## Returns `Bool.True` if the path exists on disk and is pointing at a symbolic link. ## - ## **AlreadyExists** - An entity already exists, often a file. + ## This function will not traverse symbolic links - it checks whether the path + ## itself is a symlink. + is_sym_link! : Path => Try(Bool, [PathErr(IOErr)]) + is_sym_link! = |path| + match type!(path) { + Ok(IsSymLink) => Ok(Bool.True) + Ok(_) => Ok(Bool.False) + Err(err) => Err(err) + } + + ## Unfortunately, operating system paths do not include information about which charset + ## they were originally encoded with. It's most common (but not guaranteed) that they will + ## have been encoded with the same charset as the operating system's curent locale (which + ## typically does not change after it is set during installation of the OS), so + ## this should convert a [Path] to a valid string as long as the path was created + ## with the given `Charset`. (Use `Env.charset` to get the current system charset.) ## - ## **Interrupted** - This operation was interrupted. Interrupted operations can typically be retried. + ## For a conversion to [Str] that is lossy but does not return a [Try], see + ## [display]. + ## to_inner : Path -> [Str Str, Bytes (List U8)] + + ## Assumes a path is encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8), + ## and converts it to a string using `Str.from_utf8_lossy`. ## - ## **Unsupported** - This operation is unsupported on this platform. + ## This conversion is lossy because the path may contain invalid UTF-8 bytes. If that happens, + ## any invalid bytes will be replaced with the [Unicode replacement character](https://unicode.org/glossary/#replacement_character) + ## instead of returning an error. As such, it's rarely a good idea to use the [Str] returned + ## by this function for any purpose other than displaying it to a user. ## - ## **OutOfMemory** - An operation could not be completed, because it failed to allocate enough memory. + ## When you don't know for sure what a path's encoding is, UTF-8 is a popular guess because + ## it's the default on UNIX and also is the encoding used in Roc strings. This platform also + ## automatically runs applications under the [UTF-8 code page](https://docs.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page) + ## on Windows. ## - ## **Other** - A custom error that does not fall under any other I/O error kind. - IOErr := [ - NotFound, - PermissionDenied, - BrokenPipe, - AlreadyExists, - Interrupted, - Unsupported, - OutOfMemory, - Other(Str), - ] - - ## Returns `Bool.true` if the path exists on disk and is pointing at a regular file. + ## Converting paths to strings can be an unreliable operation, because operating systems + ## don't record the paths' encodings. This means it's possible for the path to have been + ## encoded with a different character set than UTF-8 even if UTF-8 is the system default, + ## which means when [display] converts them to a string, the string may include gibberish. + ## [Here is an example.](https://unix.stackexchange.com/questions/667652/can-a-file-path-be-invalid-utf-8/667863#667863) ## - ## This function will traverse symbolic links to query information about the - ## destination file. In case of broken symbolic links this will return `Bool.false`. - is_file! : Str => Try(Bool, [PathErr(IOErr)]) + ## If you happen to know the `Charset` that was used to encode the path, you can use + ## `to_str_using_charset` (TODO) instead of [display]. + display : Path -> Str + display = |path| + match path { + FromStr(str) => str + + FromOperatingSystem(bytes) | ArbitraryBytes(bytes) => + + match Str.from_utf8(bytes) { + Ok(str) => str + Err(_) => Str.from_utf8_lossy(bytes) + } + } - ## Returns `Bool.true` if the path exists on disk and is pointing at a directory. + ## Note that the path may not be valid depending on the filesystem where it is used. + ## For example, paths containing `:` are valid on ext4 and NTFS filesystems, but not + ## on FAT ones. So if you have multiple disks on the same machine, but they have + ## different filesystems, then this path could be valid on one but invalid on another! ## - ## This function will traverse symbolic links to query information about the - ## destination file. In case of broken symbolic links this will return `Bool.false`. - is_dir! : Str => Try(Bool, [PathErr(IOErr)]) + ## It's safest to assume paths are invalid (even syntactically) until given to an operation + ## which uses them to open a file. If that operation succeeds, then the path was valid + ## (at the time). Otherwise, error handling can happen for that operation rather than validating + ## up front for a false sense of security (given symlinks, parts of a path being renamed, etc.). + from_str : Str -> Path + from_str = |str| + FromStr(str) + + # TODO add charset and to_str_using_charset function, see display comment - ## Returns `Bool.true` if the path exists on disk and is pointing at a symbolic link. + ## Return the type of the path if the path exists on disk. ## - ## This function will not traverse symbolic links - it checks whether the path - ## itself is a symlink. - is_sym_link! : Str => Try(Bool, [PathErr(IOErr)]) + ## > [`File.type`](File#type!) does the same thing, except it takes a [Str] instead of a [Path]. + type! : Path => Try([IsFile, IsDir, IsSymLink], [PathErr(IOErr)]) + type! = |path| { + Path.host_path_type!(to_bytes(path)) + .map_err(|err| PathErr(err)) + .map_ok(|path_type|{ + if path_type.is_sym_link { + IsSymLink + } else if path_type.is_dir { + IsDir + } else { + IsFile + } + }) + } } + +## TODO do this in the host, and iterate over the Str +## bytes when possible instead of always converting to +## a heap-allocated List. +to_bytes : Path -> List(U8) +to_bytes = |path| + match path { + FromOperatingSystem(bytes) => bytes + ArbitraryBytes(bytes) => bytes + FromStr(str) => Str.to_utf8(str) + } diff --git a/platform/Random.roc b/platform/Random.roc index 350ca442..fc409c3b 100644 --- a/platform/Random.roc +++ b/platform/Random.roc @@ -1,6 +1,6 @@ -Random := [].{ - IOErr := [NotFound, PermissionDenied, BrokenPipe, AlreadyExists, Interrupted, Unsupported, OutOfMemory, Other(Str)] +import IOErr exposing [IOErr] +Random := [].{ ## Generate a random 64-bit unsigned integer seed. seed_u64! : {} => Try(U64, [RandomErr(IOErr)]) diff --git a/platform/Stderr.roc b/platform/Stderr.roc index 847b0bad..a388ff4f 100644 --- a/platform/Stderr.roc +++ b/platform/Stderr.roc @@ -1,35 +1,11 @@ -Stderr := [].{ - ## **NotFound** - An entity was not found, often a file. - ## - ## **PermissionDenied** - The operation lacked the necessary privileges to complete. - ## - ## **BrokenPipe** - The operation failed because a pipe was closed. - ## - ## **AlreadyExists** - An entity already exists, often a file. - ## - ## **Interrupted** - This operation was interrupted. Interrupted operations can typically be retried. - ## - ## **Unsupported** - This operation is unsupported on this platform. This means that the operation can never succeed. - ## - ## **OutOfMemory** - An operation could not be completed, because it failed to allocate enough memory. - ## - ## **Other** - A custom error that does not fall under any other I/O error kind. - IOErr := [ - NotFound, - PermissionDenied, - BrokenPipe, - AlreadyExists, - Interrupted, - Unsupported, - OutOfMemory, - Other(Str), - ] +import IOErr exposing [IOErr] +Stderr := [].{ ## Write the given string to [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)), ## followed by a newline. ## ## > To write to `stderr` without the newline, see [Stderr.write!]. - line! : Str => {} + line! : Str => Try({}, [StderrErr(IOErr)]) ## Write the given string to [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). ## @@ -37,11 +13,11 @@ Stderr := [].{ ## so this may appear to do nothing until you write a newline! ## ## > To write to `stderr` with a newline at the end, see [Stderr.line!]. - write! : Str => {} + write! : Str => Try({}, [StderrErr(IOErr)]) - # ## Write the given bytes to [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). - # ## - # ## Most terminals will not actually display content that are written to them until they receive a newline, - # ## so this may appear to do nothing until you write a newline! - # write_bytes! : List(U8) => {} + ## Write the given bytes to [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). + ## + ## Most terminals will not actually display content that are written to them until they receive a newline, + ## so this may appear to do nothing until you write a newline! + write_bytes! : List(U8) => Try({}, [StderrErr(IOErr)]) } diff --git a/platform/Stdin.roc b/platform/Stdin.roc index 094fd2fb..acdf20d7 100644 --- a/platform/Stdin.roc +++ b/platform/Stdin.roc @@ -1,47 +1,23 @@ -Stdin := [].{ - ## **NotFound** - An entity was not found, often a file. - ## - ## **PermissionDenied** - The operation lacked the necessary privileges to complete. - ## - ## **BrokenPipe** - The operation failed because a pipe was closed. - ## - ## **AlreadyExists** - An entity already exists, often a file. - ## - ## **Interrupted** - This operation was interrupted. Interrupted operations can typically be retried. - ## - ## **Unsupported** - This operation is unsupported on this platform. This means that the operation can never succeed. - ## - ## **OutOfMemory** - An operation could not be completed, because it failed to allocate enough memory. - ## - ## **Other** - A custom error that does not fall under any other I/O error kind. - IOErr := [ - NotFound, - PermissionDenied, - BrokenPipe, - AlreadyExists, - Interrupted, - Unsupported, - OutOfMemory, - Other(Str), - ] +import IOErr exposing [IOErr] +Stdin := [].{ ## Read a line from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). ## ## > This task will block the program from continuing until `stdin` receives a newline character ## (e.g. because the user pressed Enter in the terminal), so using it can result in the appearance of the ## program having gotten stuck. It's often helpful to print a prompt first, so ## the user knows it's necessary to enter something before the program will continue. - line! : {} => Str + line! : {} => Try(Str, [EndOfFile, StdinErr(IOErr)]) - # ## Read bytes from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). - # ## This function can read no more than 16,384 bytes at a time. Use [read_to_end!] if you need more. - # ## - # ## > This is typically used in combintation with [Tty.enable_raw_mode!], - # ## which disables defaults terminal bevahiour and allows reading input - # ## without buffering until Enter key is pressed. - # bytes! : {} => List(U8) + ## Read bytes from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). + ## This function can read no more than 16,384 bytes at a time. Use [read_to_end!] if you need more. + ## + ## > This is typically used in combintation with [Tty.enable_raw_mode!], + ## which disables defaults terminal bevahiour and allows reading input + ## without buffering until Enter key is pressed. + bytes! : {} => Try(List(U8), [EndOfFile, StdinErr(IOErr)]) - # ## Read all bytes from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)) - # ## until [EOF](https://en.wikipedia.org/wiki/End-of-file) in this source. - # read_to_end! : {} => List(U8) + ## Read all bytes from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)) + ## until [EOF](https://en.wikipedia.org/wiki/End-of-file) in this source. + read_to_end! : {} => Try(List(U8), [StdinErr(IOErr)]) } diff --git a/platform/Stdout.roc b/platform/Stdout.roc index 01a2e83a..a98a51d1 100644 --- a/platform/Stdout.roc +++ b/platform/Stdout.roc @@ -1,35 +1,11 @@ -Stdout := [].{ - ## **NotFound** - An entity was not found, often a file. - ## - ## **PermissionDenied** - The operation lacked the necessary privileges to complete. - ## - ## **BrokenPipe** - The operation failed because a pipe was closed. - ## - ## **AlreadyExists** - An entity already exists, often a file. - ## - ## **Interrupted** - This operation was interrupted. Interrupted operations can typically be retried. - ## - ## **Unsupported** - This operation is unsupported on this platform. This means that the operation can never succeed. - ## - ## **OutOfMemory** - An operation could not be completed, because it failed to allocate enough memory. - ## - ## **Other** - A custom error that does not fall under any other I/O error kind. - IOErr := [ - NotFound, - PermissionDenied, - BrokenPipe, - AlreadyExists, - Interrupted, - Unsupported, - OutOfMemory, - Other(Str), - ] +import IOErr exposing [IOErr] +Stdout := [].{ ## Write the given string to [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)), ## followed by a newline. ## ## > To write to `stdout` without the newline, see [Stdout.write!]. - line! : Str => {} + line! : Str => Try({}, [StdoutErr(IOErr)]) ## Write the given string to [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)). ## @@ -37,11 +13,11 @@ Stdout := [].{ ## so this may appear to do nothing until you write a newline! ## ## > To write to `stdout` with a newline at the end, see [Stdout.line!]. - write! : Str => {} + write! : Str => Try({}, [StdoutErr(IOErr)]) - # ## Write the given bytes to [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)). - # ## - # ## Note that many terminals will not actually display content that is written to them until they receive a newline, - # ## so this may appear to do nothing until you write a newline! - # write_bytes! : List(U8) => {} + ## Write the given bytes to [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)). + ## + ## Note that many terminals will not actually display content that is written to them until they receive a newline, + ## so this may appear to do nothing until you write a newline! + write_bytes! : List(U8) => Try({}, [StdoutErr(IOErr)]) } diff --git a/platform/Utc.roc b/platform/Utc.roc index 18a7b03a..3483d70f 100644 --- a/platform/Utc.roc +++ b/platform/Utc.roc @@ -1,4 +1,23 @@ Utc := [].{ - ## Get the current time as nanoseconds since the Unix epoch (January 1, 1970). + ## Get the current UTC time as nanoseconds since the Unix epoch (January 1, 1970). now! : {} => U128 + + ## Convert nanoseconds since epoch to milliseconds since epoch. + to_millis_since_epoch : U128 -> U128 + to_millis_since_epoch = |nanos| nanos // 1_000_000 + + ## Convert milliseconds since epoch to nanoseconds since epoch. + from_millis_since_epoch : U128 -> U128 + from_millis_since_epoch = |millis| millis * 1_000_000 + + ## Calculate the difference between two timestamps in nanoseconds. + delta_as_nanos : U128, U128 -> U128 + delta_as_nanos = |a, b| if a > b { a - b } else { b - a } + + ## Calculate the difference between two timestamps in milliseconds. + delta_as_millis : U128, U128 -> U128 + delta_as_millis = |a, b| { + nanos = if a > b { a - b } else { b - a } + nanos // 1_000_000 + } } diff --git a/platform/main.roc b/platform/main.roc index 159e8a5b..69fdb0e6 100644 --- a/platform/main.roc +++ b/platform/main.roc @@ -1,11 +1,11 @@ platform "" requires {} { main! : List(Str) => Try({}, [Exit(I32), ..]) } - exposes [Cmd, Dir, Env, File, Locale, Path, Random, Sleep, Stdin, Stdout, Stderr, Tty, Utc] + exposes [Cmd, Dir, Env, File, IOErr, Locale, Path, Random, Sleep, Stdin, Stdout, Stderr, Tty, Utc] packages {} provides { "roc_main": main_for_host! } hosted { - "hosted_cmd_exec_exit_code": Cmd.exec_exit_code!, - "hosted_cmd_exec_output": Cmd.exec_output!, + "hosted_cmd_host_exec_exit_code": Cmd.host_exec_exit_code!, + "hosted_cmd_host_exec_output": Cmd.host_exec_output!, "hosted_dir_create": Dir.create!, "hosted_dir_create_all": Dir.create_all!, "hosted_dir_delete_all": Dir.delete_all!, @@ -21,17 +21,19 @@ platform "" "hosted_file_write_utf8": File.write_utf8!, "hosted_locale_all": Locale.all!, "hosted_locale_get": Locale.get!, - "hosted_path_is_dir": Path.is_dir!, - "hosted_path_is_file": Path.is_file!, - "hosted_path_is_sym_link": Path.is_sym_link!, + "hosted_path_type": Path.host_path_type!, "hosted_random_seed_u32": Random.seed_u32!, "hosted_random_seed_u64": Random.seed_u64!, "hosted_sleep_millis": Sleep.millis!, "hosted_stderr_line": Stderr.line!, "hosted_stderr_write": Stderr.write!, + "hosted_stderr_write_bytes": Stderr.write_bytes!, + "hosted_stdin_bytes": Stdin.bytes!, "hosted_stdin_line": Stdin.line!, + "hosted_stdin_read_to_end": Stdin.read_to_end!, "hosted_stdout_line": Stdout.line!, "hosted_stdout_write": Stdout.write!, + "hosted_stdout_write_bytes": Stdout.write_bytes!, "hosted_tty_disable_raw_mode": Tty.disable_raw_mode!, "hosted_tty_enable_raw_mode": Tty.enable_raw_mode!, "hosted_utc_now": Utc.now!, @@ -48,6 +50,7 @@ import Cmd import Dir import Env import File +import IOErr import Locale import Path import Random @@ -63,8 +66,8 @@ main_for_host! = |args| match main!(args) { Ok({}) => 0 Err(Exit(code)) => code - Err(other) => { - Stderr.line!("Program exited with error: ${Str.inspect(other)}") - 1 - } + Err(other) => + match Stderr.line!("Program exited with error: ${Str.inspect(other)}") { + _ => 1 + } } diff --git a/src/lib.rs b/src/lib.rs index abcd5aee..51584ced 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ use core::mem::ManuallyDrop; use std::ffi::{c_char, c_void, CStr}; use std::fs; -use std::io::{self, BufRead, Write}; +use std::io::{self, BufRead, Read, Write}; use std::sync::atomic::{AtomicBool, Ordering}; use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; @@ -14,6 +14,108 @@ mod roc_platform_abi; use crate::roc_platform_abi::*; +// RustGlue generates numbered names for anonymous Roc records and result types. +// Keep those generated names localized here so host code can use API-level names. +type CmdExitResult = TryType0; +type CmdExitResultPayload = TryType0Payload; +type CmdExitResultTag = TryType0Tag; +type CmdIOErr = IOErrType1; +type CmdIOErrPayload = IOErrType1Payload; +type CmdIOErrTag = IOErrType1Tag; +type CmdOutputResult = TryType7; +type CmdOutputResultPayload = TryType7Payload; +type CmdOutputResultTag = TryType7Tag; +type CmdOutputFailureResult = TryType8; +type CmdOutputFailureResultPayload = TryType8Payload; +type CmdOutputFailureResultTag = TryType8Tag; +type CmdOutputFailure = AnonStruct9; +type CmdOutputSuccess = AnonStruct12; + +type DirUnitResult = TryType13; +type DirUnitResultPayload = TryType13Payload; +type DirUnitResultTag = TryType13Tag; +type DirIOErr = IOErrType15; +type DirIOErrPayload = IOErrType15Payload; +type DirIOErrTag = IOErrType15Tag; +type DirListResult = TryType18; +type DirListResultPayload = TryType18Payload; +type DirListResultTag = TryType18Tag; + +type EnvVarResult = TryType20; +type EnvVarResultPayload = TryType20Payload; +type EnvVarResultTag = TryType20Tag; +type EnvCwdResult = TryType23; +type EnvCwdResultPayload = TryType23Payload; +type EnvCwdResultTag = TryType23Tag; +type EnvExePathResult = TryType26; +type EnvExePathResultPayload = TryType26Payload; +type EnvExePathResultTag = TryType26Tag; + +type FileBytesResult = TryType28; +type FileBytesResultPayload = TryType28Payload; +type FileBytesResultTag = TryType28Tag; +type FileIOErr = IOErrType30; +type FileIOErrPayload = IOErrType30Payload; +type FileIOErrTag = IOErrType30Tag; +type FileUnitResult = TryType34; +type FileUnitResultPayload = TryType34Payload; +type FileUnitResultTag = TryType34Tag; +type FileStrResult = TryType36; +type FileStrResultPayload = TryType36Payload; +type FileStrResultTag = TryType36Tag; + +type LocaleGetResult = TryType37; +type LocaleGetResultPayload = TryType37Payload; +type LocaleGetResultTag = TryType37Tag; + +type PathTypeResult = TryType41; +type PathTypeResultPayload = TryType41Payload; +type PathTypeResultTag = TryType41Tag; +type PathIOErr = IOErrType42; +type PathIOErrPayload = IOErrType42Payload; +type PathIOErrTag = IOErrType42Tag; +type PathInfo = AnonStruct44; + +type RandomU64Result = TryType48; +type RandomU64ResultPayload = TryType48Payload; +type RandomU64ResultTag = TryType48Tag; +type RandomIOErr = IOErrType50; +type RandomIOErrPayload = IOErrType50Payload; +type RandomIOErrTag = IOErrType50Tag; +type RandomU32Result = TryType54; +type RandomU32ResultPayload = TryType54Payload; +type RandomU32ResultTag = TryType54Tag; + +type StderrUnitResult = TryType58; +type StderrUnitResultPayload = TryType58Payload; +type StderrUnitResultTag = TryType58Tag; +type StderrIOErr = IOErrType60; +type StderrIOErrPayload = IOErrType60Payload; +type StderrIOErrTag = IOErrType60Tag; + +type StdinLineResult = TryType65; +type StdinLineResultPayload = TryType65Payload; +type StdinLineResultTag = TryType65Tag; +type StdinReadErr = EndOfFileOrStdinErr; +type StdinReadErrPayload = EndOfFileOrStdinErrPayload; +type StdinReadErrTag = EndOfFileOrStdinErrTag; +type StdinIOErr = IOErrType67; +type StdinIOErrPayload = IOErrType67Payload; +type StdinIOErrTag = IOErrType67Tag; +type StdinBytesResult = TryType70; +type StdinBytesResultPayload = TryType70Payload; +type StdinBytesResultTag = TryType70Tag; +type StdinReadToEndResult = TryType73; +type StdinReadToEndResultPayload = TryType73Payload; +type StdinReadToEndResultTag = TryType73Tag; + +type StdoutUnitResult = TryType75; +type StdoutUnitResultPayload = TryType75Payload; +type StdoutUnitResultTag = TryType75Tag; +type StdoutIOErr = IOErrType77; +type StdoutIOErrPayload = IOErrType77Payload; +type StdoutIOErrTag = IOErrType77Tag; + extern "C" { fn roc_main(args: RocList) -> i32; } @@ -97,6 +199,13 @@ define_common_io_err!( CmdIOErrTag, CmdIOErrPayload ); +define_common_io_err!( + dir_io_err_from_io, + dir_io_err_other, + DirIOErr, + DirIOErrTag, + DirIOErrPayload +); define_common_io_err!( file_io_err_from_io, file_io_err_other, @@ -118,50 +227,27 @@ define_common_io_err!( RandomIOErrTag, RandomIOErrPayload ); - -fn dir_io_err_other(message: &str, roc_host: &RocHost) -> DirIOErr { - DirIOErr { - payload: DirIOErrPayload { - other: ManuallyDrop::new(RocStr::from_str(message, roc_host)), - }, - tag: DirIOErrTag::Other, - } -} - -fn dir_io_err_from_io(error: &io::Error, roc_host: &RocHost) -> DirIOErr { - if let Some(errno) = error.raw_os_error() { - if errno == libc::ENOTDIR { - return DirIOErr { - payload: DirIOErrPayload { not_adirectory: [] }, - tag: DirIOErrTag::NotADirectory, - }; - } - if errno == libc::ENOTEMPTY { - return DirIOErr { - payload: DirIOErrPayload { not_empty: [] }, - tag: DirIOErrTag::NotEmpty, - }; - } - } - - match error.kind() { - io::ErrorKind::AlreadyExists => DirIOErr { - payload: DirIOErrPayload { already_exists: [] }, - tag: DirIOErrTag::AlreadyExists, - }, - io::ErrorKind::NotFound => DirIOErr { - payload: DirIOErrPayload { not_found: [] }, - tag: DirIOErrTag::NotFound, - }, - io::ErrorKind::PermissionDenied => DirIOErr { - payload: DirIOErrPayload { - permission_denied: [], - }, - tag: DirIOErrTag::PermissionDenied, - }, - _ => dir_io_err_other(&error.to_string(), roc_host), - } -} +define_common_io_err!( + stderr_io_err_from_io, + stderr_io_err_other, + StderrIOErr, + StderrIOErrTag, + StderrIOErrPayload +); +define_common_io_err!( + stdin_io_err_from_io, + stdin_io_err_other, + StdinIOErr, + StdinIOErrTag, + StdinIOErrPayload +); +define_common_io_err!( + stdout_io_err_from_io, + stdout_io_err_other, + StdoutIOErr, + StdoutIOErrTag, + StdoutIOErrPayload +); fn decref_roc_str_list(list: &RocList, roc_host: &RocHost) { for item in list.as_slice() { @@ -170,7 +256,7 @@ fn decref_roc_str_list(list: &RocList, roc_host: &RocHost) { list.decref(roc_host); } -fn decref_cmd(cmd: &Cmd, roc_host: &RocHost) { +fn decref_host_cmd_arg(cmd: &Cmd, roc_host: &RocHost) { decref_roc_str_list(&cmd.args, roc_host); decref_roc_str_list(&cmd.envs, roc_host); cmd.program.decref(roc_host); @@ -196,195 +282,378 @@ fn cmd_to_std(cmd: &Cmd) -> std::process::Command { std_cmd } -fn roc_str_lossy(bytes: &[u8], roc_host: &RocHost) -> RocStr { - RocStr::from_str(String::from_utf8_lossy(bytes).as_ref(), roc_host) +fn try_cmd_exit_ok(value: i32) -> CmdExitResult { + CmdExitResult { + payload: CmdExitResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: CmdExitResultTag::Ok, + } +} + +fn try_cmd_exit_err(error: CmdIOErr) -> CmdExitResult { + CmdExitResult { + payload: CmdExitResultPayload { + err: ManuallyDrop::new(error), + }, + tag: CmdExitResultTag::Err, + } } -fn try_cmd_exit_ok(value: i32) -> TryType0 { - TryType0 { - payload: TryType0Payload { +fn try_cmd_output_ok(value: CmdOutputSuccess) -> CmdOutputResult { + CmdOutputResult { + payload: CmdOutputResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType0Tag::Ok, + tag: CmdOutputResultTag::Ok, } } -fn try_cmd_exit_err(error: CmdIOErr) -> TryType0 { - TryType0 { - payload: TryType0Payload { +fn try_cmd_output_err(error: CmdOutputFailureResult) -> CmdOutputResult { + CmdOutputResult { + payload: CmdOutputResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType0Tag::Err, + tag: CmdOutputResultTag::Err, } } -fn try_cmd_output_ok(value: AnonStruct11) -> TryType8 { - TryType8 { - payload: TryType8Payload { +fn try_cmd_output_failure_ok(value: CmdOutputFailure) -> CmdOutputFailureResult { + CmdOutputFailureResult { + payload: CmdOutputFailureResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType8Tag::Ok, + tag: CmdOutputFailureResultTag::Ok, } } -fn try_cmd_output_err(error: CmdErrOrNonZeroExit) -> TryType8 { - TryType8 { - payload: TryType8Payload { +fn try_cmd_output_failure_err(error: CmdIOErr) -> CmdOutputFailureResult { + CmdOutputFailureResult { + payload: CmdOutputFailureResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType8Tag::Err, + tag: CmdOutputFailureResultTag::Err, } } -fn try_dir_unit_ok() -> TryType12 { - TryType12 { - payload: TryType12Payload { +fn try_dir_unit_ok() -> DirUnitResult { + DirUnitResult { + payload: DirUnitResultPayload { ok: ManuallyDrop::new(()), }, - tag: TryType12Tag::Ok, + tag: DirUnitResultTag::Ok, + } +} + +fn try_dir_unit_err(error: DirIOErr) -> DirUnitResult { + DirUnitResult { + payload: DirUnitResultPayload { + err: ManuallyDrop::new(error), + }, + tag: DirUnitResultTag::Err, } } -fn try_dir_unit_err(error: DirIOErr) -> TryType12 { - TryType12 { - payload: TryType12Payload { +fn try_dir_list_ok(value: RocList) -> DirListResult { + DirListResult { + payload: DirListResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: DirListResultTag::Ok, + } +} + +fn try_dir_list_err(error: DirIOErr) -> DirListResult { + DirListResult { + payload: DirListResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType12Tag::Err, + tag: DirListResultTag::Err, } } -fn try_dir_list_ok(value: RocList) -> TryType17 { - TryType17 { - payload: TryType17Payload { +fn try_env_str_ok(value: RocStr) -> EnvVarResult { + EnvVarResult { + payload: EnvVarResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType17Tag::Ok, + tag: EnvVarResultTag::Ok, } } -fn try_dir_list_err(error: DirIOErr) -> TryType17 { - TryType17 { - payload: TryType17Payload { +fn try_env_str_err(error: RocStr) -> EnvVarResult { + EnvVarResult { + payload: EnvVarResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType17Tag::Err, + tag: EnvVarResultTag::Err, + } +} + +fn try_env_cwd_ok(value: RocStr) -> EnvCwdResult { + EnvCwdResult { + payload: EnvCwdResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: EnvCwdResultTag::Ok, + } +} + +fn try_env_cwd_err() -> EnvCwdResult { + EnvCwdResult { + payload: EnvCwdResultPayload { + err: ManuallyDrop::new(core::ptr::null_mut()), + }, + tag: EnvCwdResultTag::Err, + } +} + +fn try_env_exe_path_ok(value: RocStr) -> EnvExePathResult { + EnvExePathResult { + payload: EnvExePathResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: EnvExePathResultTag::Ok, + } +} + +fn try_env_exe_path_err() -> EnvExePathResult { + EnvExePathResult { + payload: EnvExePathResultPayload { + err: ManuallyDrop::new(core::ptr::null_mut()), + }, + tag: EnvExePathResultTag::Err, } } -fn try_file_bytes_ok(value: RocListWith) -> TryType21 { - TryType21 { - payload: TryType21Payload { +fn try_file_bytes_ok(value: RocListWith) -> FileBytesResult { + FileBytesResult { + payload: FileBytesResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType21Tag::Ok, + tag: FileBytesResultTag::Ok, } } -fn try_file_bytes_err(error: FileIOErr) -> TryType21 { - TryType21 { - payload: TryType21Payload { +fn try_file_bytes_err(error: FileIOErr) -> FileBytesResult { + FileBytesResult { + payload: FileBytesResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType21Tag::Err, + tag: FileBytesResultTag::Err, } } -fn try_file_unit_ok() -> TryType27 { - TryType27 { - payload: TryType27Payload { +fn try_file_unit_ok() -> FileUnitResult { + FileUnitResult { + payload: FileUnitResultPayload { ok: ManuallyDrop::new(()), }, - tag: TryType27Tag::Ok, + tag: FileUnitResultTag::Ok, + } +} + +fn try_file_unit_err(error: FileIOErr) -> FileUnitResult { + FileUnitResult { + payload: FileUnitResultPayload { + err: ManuallyDrop::new(error), + }, + tag: FileUnitResultTag::Err, + } +} + +fn try_file_str_ok(value: RocStr) -> FileStrResult { + FileStrResult { + payload: FileStrResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: FileStrResultTag::Ok, + } +} + +fn try_file_str_err(error: FileIOErr) -> FileStrResult { + FileStrResult { + payload: FileStrResultPayload { + err: ManuallyDrop::new(error), + }, + tag: FileStrResultTag::Err, + } +} + +fn try_locale_get_ok(value: RocStr) -> LocaleGetResult { + LocaleGetResult { + payload: LocaleGetResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: LocaleGetResultTag::Ok, + } +} + +fn try_path_type_ok(value: PathInfo) -> PathTypeResult { + PathTypeResult { + payload: PathTypeResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: PathTypeResultTag::Ok, } } -fn try_file_unit_err(error: FileIOErr) -> TryType27 { - TryType27 { - payload: TryType27Payload { +fn try_path_type_err(error: PathIOErr) -> PathTypeResult { + PathTypeResult { + payload: PathTypeResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType27Tag::Err, + tag: PathTypeResultTag::Err, } } -fn try_file_str_ok(value: RocStr) -> TryType29 { - TryType29 { - payload: TryType29Payload { +fn try_random_u64_ok(value: u64) -> RandomU64Result { + RandomU64Result { + payload: RandomU64ResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType29Tag::Ok, + tag: RandomU64ResultTag::Ok, + } +} + +fn try_random_u64_err(error: RandomIOErr) -> RandomU64Result { + RandomU64Result { + payload: RandomU64ResultPayload { + err: ManuallyDrop::new(error), + }, + tag: RandomU64ResultTag::Err, + } +} + +fn try_random_u32_ok(value: u32) -> RandomU32Result { + RandomU32Result { + payload: RandomU32ResultPayload { + ok: ManuallyDrop::new(value), + }, + tag: RandomU32ResultTag::Ok, + } +} + +fn try_random_u32_err(error: RandomIOErr) -> RandomU32Result { + RandomU32Result { + payload: RandomU32ResultPayload { + err: ManuallyDrop::new(error), + }, + tag: RandomU32ResultTag::Err, + } +} + +fn try_stderr_unit_ok() -> StderrUnitResult { + StderrUnitResult { + payload: StderrUnitResultPayload { + ok: ManuallyDrop::new(()), + }, + tag: StderrUnitResultTag::Ok, } } -fn try_file_str_err(error: FileIOErr) -> TryType29 { - TryType29 { - payload: TryType29Payload { +fn try_stderr_unit_err(error: StderrIOErr) -> StderrUnitResult { + StderrUnitResult { + payload: StderrUnitResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType29Tag::Err, + tag: StderrUnitResultTag::Err, + } +} + +fn stdin_eof_or_err_eof() -> StdinReadErr { + StdinReadErr { + payload: StdinReadErrPayload { end_of_file: [] }, + tag: StdinReadErrTag::EndOfFile, + } +} + +fn stdin_eof_or_err_io(error: StdinIOErr) -> StdinReadErr { + StdinReadErr { + payload: StdinReadErrPayload { + stdin_err: ManuallyDrop::new(error), + }, + tag: StdinReadErrTag::StdinErr, } } -fn try_path_bool_ok(value: bool) -> TryType32 { - TryType32 { - payload: TryType32Payload { +fn try_stdin_line_ok(value: RocStr) -> StdinLineResult { + StdinLineResult { + payload: StdinLineResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType32Tag::Ok, + tag: StdinLineResultTag::Ok, } } -fn try_path_bool_err(error: PathIOErr) -> TryType32 { - TryType32 { - payload: TryType32Payload { +fn try_stdin_line_err(error: StdinReadErr) -> StdinLineResult { + StdinLineResult { + payload: StdinLineResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType32Tag::Err, + tag: StdinLineResultTag::Err, } } -fn try_random_u64_ok(value: u64) -> TryType37 { - TryType37 { - payload: TryType37Payload { +fn try_stdin_bytes_ok(value: RocListWith) -> StdinBytesResult { + StdinBytesResult { + payload: StdinBytesResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType37Tag::Ok, + tag: StdinBytesResultTag::Ok, } } -fn try_random_u64_err(error: RandomIOErr) -> TryType37 { - TryType37 { - payload: TryType37Payload { +fn try_stdin_bytes_err(error: StdinReadErr) -> StdinBytesResult { + StdinBytesResult { + payload: StdinBytesResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType37Tag::Err, + tag: StdinBytesResultTag::Err, } } -fn try_random_u32_ok(value: u32) -> TryType43 { - TryType43 { - payload: TryType43Payload { +fn try_stdin_read_to_end_ok(value: RocListWith) -> StdinReadToEndResult { + StdinReadToEndResult { + payload: StdinReadToEndResultPayload { ok: ManuallyDrop::new(value), }, - tag: TryType43Tag::Ok, + tag: StdinReadToEndResultTag::Ok, + } +} + +fn try_stdin_read_to_end_err(error: StdinIOErr) -> StdinReadToEndResult { + StdinReadToEndResult { + payload: StdinReadToEndResultPayload { + err: ManuallyDrop::new(error), + }, + tag: StdinReadToEndResultTag::Err, + } +} + +fn try_stdout_unit_ok() -> StdoutUnitResult { + StdoutUnitResult { + payload: StdoutUnitResultPayload { + ok: ManuallyDrop::new(()), + }, + tag: StdoutUnitResultTag::Ok, } } -fn try_random_u32_err(error: RandomIOErr) -> TryType43 { - TryType43 { - payload: TryType43Payload { +fn try_stdout_unit_err(error: StdoutIOErr) -> StdoutUnitResult { + StdoutUnitResult { + payload: StdoutUnitResultPayload { err: ManuallyDrop::new(error), }, - tag: TryType43Tag::Err, + tag: StdoutUnitResultTag::Err, } } #[no_mangle] -pub extern "C" fn hosted_cmd_exec_exit_code(cmd: Cmd) -> TryType0 { +pub extern "C" fn hosted_cmd_host_exec_exit_code(cmd: Cmd) -> CmdExitResult { let roc_host = roc_host(); let mut std_cmd = cmd_to_std(&cmd); - decref_cmd(&cmd, roc_host); + decref_host_cmd_arg(&cmd, roc_host); match std_cmd.status() { Ok(status) => match status.code() { @@ -396,52 +665,41 @@ pub extern "C" fn hosted_cmd_exec_exit_code(cmd: Cmd) -> TryType0 { } #[no_mangle] -pub extern "C" fn hosted_cmd_exec_output(cmd: Cmd) -> TryType8 { +pub extern "C" fn hosted_cmd_host_exec_output(cmd: Cmd) -> CmdOutputResult { let roc_host = roc_host(); let mut std_cmd = cmd_to_std(&cmd); - decref_cmd(&cmd, roc_host); + decref_host_cmd_arg(&cmd, roc_host); match std_cmd.output() { Ok(output) => { - let stdout = roc_str_lossy(&output.stdout, roc_host); - let stderr = roc_str_lossy(&output.stderr, roc_host); + let stdout_bytes = RocListWith::::from_slice(&output.stdout, roc_host); + let stderr_bytes = RocListWith::::from_slice(&output.stderr, roc_host); match output.status.code() { - Some(0) => try_cmd_output_ok(AnonStruct11 { - stderr_utf8_lossy: stderr, - stdout_utf8: stdout, - }), - Some(exit_code) => try_cmd_output_err(CmdErrOrNonZeroExit { - payload: CmdErrOrNonZeroExitPayload { - non_zero_exit: ManuallyDrop::new(AnonStruct10 { - stderr_utf8_lossy: stderr, - stdout_utf8_lossy: stdout, - exit_code, - }), - }, - tag: CmdErrOrNonZeroExitTag::NonZeroExit, + Some(0) => try_cmd_output_ok(CmdOutputSuccess { + stderr_bytes, + stdout_bytes, }), + Some(exit_code) => { + try_cmd_output_err(try_cmd_output_failure_ok(CmdOutputFailure { + stderr_bytes, + stdout_bytes, + exit_code, + })) + } None => { - stdout.decref(roc_host); - stderr.decref(roc_host); - try_cmd_output_err(CmdErrOrNonZeroExit { - payload: CmdErrOrNonZeroExitPayload { - cmd_err: ManuallyDrop::new(cmd_io_err_other( - "Process was killed by signal", - roc_host, - )), - }, - tag: CmdErrOrNonZeroExitTag::CmdErr, - }) + stdout_bytes.decref(roc_host); + stderr_bytes.decref(roc_host); + try_cmd_output_err(try_cmd_output_failure_err(cmd_io_err_other( + "Process was killed by signal", + roc_host, + ))) } } } - Err(error) => try_cmd_output_err(CmdErrOrNonZeroExit { - payload: CmdErrOrNonZeroExitPayload { - cmd_err: ManuallyDrop::new(cmd_io_err_from_io(&error, roc_host)), - }, - tag: CmdErrOrNonZeroExitTag::CmdErr, - }), + Err(error) => try_cmd_output_err(try_cmd_output_failure_err(cmd_io_err_from_io( + &error, roc_host, + ))), } } @@ -452,7 +710,7 @@ fn path_from_roc_str(path: RocStr, roc_host: &RocHost) -> String { } #[no_mangle] -pub extern "C" fn hosted_dir_create(path: RocStr) -> TryType12 { +pub extern "C" fn hosted_dir_create(path: RocStr) -> DirUnitResult { let roc_host = roc_host(); match fs::create_dir(path_from_roc_str(path, roc_host)) { Ok(()) => try_dir_unit_ok(), @@ -461,7 +719,7 @@ pub extern "C" fn hosted_dir_create(path: RocStr) -> TryType12 { } #[no_mangle] -pub extern "C" fn hosted_dir_create_all(path: RocStr) -> TryType12 { +pub extern "C" fn hosted_dir_create_all(path: RocStr) -> DirUnitResult { let roc_host = roc_host(); match fs::create_dir_all(path_from_roc_str(path, roc_host)) { Ok(()) => try_dir_unit_ok(), @@ -470,7 +728,7 @@ pub extern "C" fn hosted_dir_create_all(path: RocStr) -> TryType12 { } #[no_mangle] -pub extern "C" fn hosted_dir_delete_all(path: RocStr) -> TryType12 { +pub extern "C" fn hosted_dir_delete_all(path: RocStr) -> DirUnitResult { let roc_host = roc_host(); match fs::remove_dir_all(path_from_roc_str(path, roc_host)) { Ok(()) => try_dir_unit_ok(), @@ -479,7 +737,7 @@ pub extern "C" fn hosted_dir_delete_all(path: RocStr) -> TryType12 { } #[no_mangle] -pub extern "C" fn hosted_dir_delete_empty(path: RocStr) -> TryType12 { +pub extern "C" fn hosted_dir_delete_empty(path: RocStr) -> DirUnitResult { let roc_host = roc_host(); match fs::remove_dir(path_from_roc_str(path, roc_host)) { Ok(()) => try_dir_unit_ok(), @@ -488,7 +746,7 @@ pub extern "C" fn hosted_dir_delete_empty(path: RocStr) -> TryType12 { } #[no_mangle] -pub extern "C" fn hosted_dir_list(path: RocStr) -> TryType17 { +pub extern "C" fn hosted_dir_list(path: RocStr) -> DirListResult { let roc_host = roc_host(); match fs::read_dir(path_from_roc_str(path, roc_host)) { Ok(read_dir) => { @@ -514,33 +772,40 @@ pub extern "C" fn hosted_dir_list(path: RocStr) -> TryType17 { } #[no_mangle] -pub extern "C" fn hosted_env_cwd() -> RocStr { +pub extern "C" fn hosted_env_cwd() -> EnvCwdResult { let roc_host = roc_host(); - let cwd = std::env::current_dir() - .map(|path| path.to_string_lossy().into_owned()) - .unwrap_or_default(); - RocStr::from_str(&cwd, roc_host) + match std::env::current_dir() { + Ok(path) => try_env_cwd_ok(RocStr::from_str(path.to_string_lossy().as_ref(), roc_host)), + Err(_) => try_env_cwd_err(), + } } #[no_mangle] -pub extern "C" fn hosted_env_exe_path() -> RocStr { +pub extern "C" fn hosted_env_exe_path() -> EnvExePathResult { let roc_host = roc_host(); - let exe_path = std::env::current_exe() - .map(|path| path.to_string_lossy().into_owned()) - .unwrap_or_default(); - RocStr::from_str(&exe_path, roc_host) + match std::env::current_exe() { + Ok(path) => { + try_env_exe_path_ok(RocStr::from_str(path.to_string_lossy().as_ref(), roc_host)) + } + Err(_) => try_env_exe_path_err(), + } } #[no_mangle] -pub extern "C" fn hosted_env_var(name: RocStr) -> RocStr { +pub extern "C" fn hosted_env_var(name: RocStr) -> EnvVarResult { let roc_host = roc_host(); - let value = std::env::var(name.as_str()).unwrap_or_default(); - name.decref(roc_host); - RocStr::from_str(&value, roc_host) + let key = name.as_str().to_owned(); + match std::env::var_os(&key) { + Some(value) => { + name.decref(roc_host); + try_env_str_ok(RocStr::from_str(value.to_string_lossy().as_ref(), roc_host)) + } + None => try_env_str_err(name), + } } #[no_mangle] -pub extern "C" fn hosted_file_delete(path: RocStr) -> TryType27 { +pub extern "C" fn hosted_file_delete(path: RocStr) -> FileUnitResult { let roc_host = roc_host(); match fs::remove_file(path_from_roc_str(path, roc_host)) { Ok(()) => try_file_unit_ok(), @@ -549,7 +814,7 @@ pub extern "C" fn hosted_file_delete(path: RocStr) -> TryType27 { } #[no_mangle] -pub extern "C" fn hosted_file_read_bytes(path: RocStr) -> TryType21 { +pub extern "C" fn hosted_file_read_bytes(path: RocStr) -> FileBytesResult { let roc_host = roc_host(); match fs::read(path_from_roc_str(path, roc_host)) { Ok(bytes) => try_file_bytes_ok(RocListWith::::from_slice(&bytes, roc_host)), @@ -558,7 +823,7 @@ pub extern "C" fn hosted_file_read_bytes(path: RocStr) -> TryType21 { } #[no_mangle] -pub extern "C" fn hosted_file_read_utf8(path: RocStr) -> TryType29 { +pub extern "C" fn hosted_file_read_utf8(path: RocStr) -> FileStrResult { let roc_host = roc_host(); match fs::read_to_string(path_from_roc_str(path, roc_host)) { Ok(content) => try_file_str_ok(RocStr::from_str(&content, roc_host)), @@ -570,7 +835,7 @@ pub extern "C" fn hosted_file_read_utf8(path: RocStr) -> TryType29 { pub extern "C" fn hosted_file_write_bytes( path: RocStr, bytes: RocListWith, -) -> TryType27 { +) -> FileUnitResult { let roc_host = roc_host(); let path_string = path_from_roc_str(path, roc_host); let result = fs::write(path_string, bytes.as_slice()); @@ -583,7 +848,7 @@ pub extern "C" fn hosted_file_write_bytes( } #[no_mangle] -pub extern "C" fn hosted_file_write_utf8(path: RocStr, content: RocStr) -> TryType27 { +pub extern "C" fn hosted_file_write_utf8(path: RocStr, content: RocStr) -> FileUnitResult { let roc_host = roc_host(); let path_string = path_from_roc_str(path, roc_host); let content_string = content.as_str().to_owned(); @@ -665,47 +930,52 @@ pub extern "C" fn hosted_locale_all() -> RocList { } #[no_mangle] -pub extern "C" fn hosted_locale_get() -> RocStr { +pub extern "C" fn hosted_locale_get() -> LocaleGetResult { let roc_host = roc_host(); - RocStr::from_str(&locale_get_string(), roc_host) + try_locale_get_ok(RocStr::from_str(&locale_get_string(), roc_host)) } -fn path_bool_result( - path: RocStr, - check: impl FnOnce(&std::path::Path) -> io::Result, -) -> TryType32 { - let roc_host = roc_host(); - let path_string = path_from_roc_str(path, roc_host); - match check(std::path::Path::new(&path_string)) { - Ok(value) => try_path_bool_ok(value), - Err(error) => try_path_bool_err(path_io_err_from_io(&error, roc_host)), +fn path_buf_from_roc_bytes( + bytes: RocListWith, + roc_host: &RocHost, +) -> std::path::PathBuf { + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + + let path = std::ffi::OsStr::from_bytes(bytes.as_slice()).to_owned(); + bytes.decref(roc_host); + std::path::PathBuf::from(path) } -} - -#[no_mangle] -pub extern "C" fn hosted_path_is_dir(path: RocStr) -> TryType32 { - path_bool_result(path, |path| { - path.symlink_metadata().map(|metadata| metadata.is_dir()) - }) -} -#[no_mangle] -pub extern "C" fn hosted_path_is_file(path: RocStr) -> TryType32 { - path_bool_result(path, |path| { - path.symlink_metadata().map(|metadata| metadata.is_file()) - }) + #[cfg(not(unix))] + { + let path = String::from_utf8_lossy(bytes.as_slice()).into_owned(); + bytes.decref(roc_host); + std::path::PathBuf::from(path) + } } #[no_mangle] -pub extern "C" fn hosted_path_is_sym_link(path: RocStr) -> TryType32 { - path_bool_result(path, |path| { - path.symlink_metadata() - .map(|metadata| metadata.file_type().is_symlink()) - }) +pub extern "C" fn hosted_path_type(path: RocListWith) -> PathTypeResult { + let roc_host = roc_host(); + let path = path_buf_from_roc_bytes(path, roc_host); + + match path.symlink_metadata() { + Ok(metadata) => { + let file_type = metadata.file_type(); + try_path_type_ok(PathInfo { + is_dir: metadata.is_dir(), + is_file: metadata.is_file(), + is_sym_link: file_type.is_symlink(), + }) + } + Err(error) => try_path_type_err(path_io_err_from_io(&error, roc_host)), + } } #[no_mangle] -pub extern "C" fn hosted_random_seed_u32() -> TryType43 { +pub extern "C" fn hosted_random_seed_u32() -> RandomU32Result { let roc_host = roc_host(); let mut bytes = [0u8; 4]; match getrandom::getrandom(&mut bytes) { @@ -718,7 +988,7 @@ pub extern "C" fn hosted_random_seed_u32() -> TryType43 { } #[no_mangle] -pub extern "C" fn hosted_random_seed_u64() -> TryType37 { +pub extern "C" fn hosted_random_seed_u64() -> RandomU64Result { let roc_host = roc_host(); let mut bytes = [0u8; 8]; match getrandom::getrandom(&mut bytes) { @@ -736,42 +1006,139 @@ pub extern "C" fn hosted_sleep_millis(millis: u64) { } #[no_mangle] -pub extern "C" fn hosted_stderr_line(message: RocStr) { +pub extern "C" fn hosted_stderr_line(message: RocStr) -> StderrUnitResult { let roc_host = roc_host(); - let _ = writeln!(io::stderr(), "{}", message.as_str()); + let result = { + let mut stderr = io::stderr().lock(); + writeln!(stderr, "{}", message.as_str()) + }; message.decref(roc_host); + + match result { + Ok(()) => try_stderr_unit_ok(), + Err(error) => try_stderr_unit_err(stderr_io_err_from_io(&error, roc_host)), + } } #[no_mangle] -pub extern "C" fn hosted_stderr_write(message: RocStr) { +pub extern "C" fn hosted_stderr_write(message: RocStr) -> StderrUnitResult { let roc_host = roc_host(); - let _ = write!(io::stderr(), "{}", message.as_str()); - let _ = io::stderr().flush(); + let result = { + let mut stderr = io::stderr().lock(); + write!(stderr, "{}", message.as_str()).and_then(|()| stderr.flush()) + }; message.decref(roc_host); + + match result { + Ok(()) => try_stderr_unit_ok(), + Err(error) => try_stderr_unit_err(stderr_io_err_from_io(&error, roc_host)), + } +} + +#[no_mangle] +pub extern "C" fn hosted_stderr_write_bytes(bytes: RocListWith) -> StderrUnitResult { + let roc_host = roc_host(); + let result = { + let mut stderr = io::stderr().lock(); + stderr + .write_all(bytes.as_slice()) + .and_then(|()| stderr.flush()) + }; + bytes.decref(roc_host); + + match result { + Ok(()) => try_stderr_unit_ok(), + Err(error) => try_stderr_unit_err(stderr_io_err_from_io(&error, roc_host)), + } } #[no_mangle] -pub extern "C" fn hosted_stdin_line() -> RocStr { +pub extern "C" fn hosted_stdin_line() -> StdinLineResult { let roc_host = roc_host(); let mut line = String::new(); - let _ = io::stdin().lock().read_line(&mut line); - let trimmed = line.trim_end_matches('\n').trim_end_matches('\r'); - RocStr::from_str(trimmed, roc_host) + match io::stdin().lock().read_line(&mut line) { + Ok(0) => try_stdin_line_err(stdin_eof_or_err_eof()), + Ok(_) => { + let trimmed = line.trim_end_matches('\n').trim_end_matches('\r'); + try_stdin_line_ok(RocStr::from_str(trimmed, roc_host)) + } + Err(error) => { + try_stdin_line_err(stdin_eof_or_err_io(stdin_io_err_from_io(&error, roc_host))) + } + } +} + +#[no_mangle] +pub extern "C" fn hosted_stdin_bytes() -> StdinBytesResult { + let roc_host = roc_host(); + let mut buffer = [0u8; 16_384]; + match io::stdin().lock().read(&mut buffer) { + Ok(0) => try_stdin_bytes_err(stdin_eof_or_err_eof()), + Ok(bytes_read) => try_stdin_bytes_ok(RocListWith::::from_slice( + &buffer[..bytes_read], + roc_host, + )), + Err(error) => { + try_stdin_bytes_err(stdin_eof_or_err_io(stdin_io_err_from_io(&error, roc_host))) + } + } +} + +#[no_mangle] +pub extern "C" fn hosted_stdin_read_to_end() -> StdinReadToEndResult { + let roc_host = roc_host(); + let mut buffer = Vec::new(); + match io::stdin().lock().read_to_end(&mut buffer) { + Ok(_) => try_stdin_read_to_end_ok(RocListWith::::from_slice(&buffer, roc_host)), + Err(error) => try_stdin_read_to_end_err(stdin_io_err_from_io(&error, roc_host)), + } } #[no_mangle] -pub extern "C" fn hosted_stdout_line(message: RocStr) { +pub extern "C" fn hosted_stdout_line(message: RocStr) -> StdoutUnitResult { let roc_host = roc_host(); - let _ = writeln!(io::stdout(), "{}", message.as_str()); + let result = { + let mut stdout = io::stdout().lock(); + writeln!(stdout, "{}", message.as_str()) + }; message.decref(roc_host); + + match result { + Ok(()) => try_stdout_unit_ok(), + Err(error) => try_stdout_unit_err(stdout_io_err_from_io(&error, roc_host)), + } } #[no_mangle] -pub extern "C" fn hosted_stdout_write(message: RocStr) { +pub extern "C" fn hosted_stdout_write(message: RocStr) -> StdoutUnitResult { let roc_host = roc_host(); - let _ = write!(io::stdout(), "{}", message.as_str()); - let _ = io::stdout().flush(); + let result = { + let mut stdout = io::stdout().lock(); + write!(stdout, "{}", message.as_str()).and_then(|()| stdout.flush()) + }; message.decref(roc_host); + + match result { + Ok(()) => try_stdout_unit_ok(), + Err(error) => try_stdout_unit_err(stdout_io_err_from_io(&error, roc_host)), + } +} + +#[no_mangle] +pub extern "C" fn hosted_stdout_write_bytes(bytes: RocListWith) -> StdoutUnitResult { + let roc_host = roc_host(); + let result = { + let mut stdout = io::stdout().lock(); + stdout + .write_all(bytes.as_slice()) + .and_then(|()| stdout.flush()) + }; + bytes.decref(roc_host); + + match result { + Ok(()) => try_stdout_unit_ok(), + Err(error) => try_stdout_unit_err(stdout_io_err_from_io(&error, roc_host)), + } } #[no_mangle] diff --git a/src/roc_platform_abi.rs b/src/roc_platform_abi.rs index 3a2df6c7..7a415506 100644 --- a/src/roc_platform_abi.rs +++ b/src/roc_platform_abi.rs @@ -434,7 +434,7 @@ impl RocStr { let prev = (*rc).fetch_sub(1, Ordering::Relaxed); if prev == 1 { let ptr_width = core::mem::size_of::(); - let base = (alloc_ptr as *mut u8).sub(ptr_width) as *mut c_void; + let base = alloc_ptr.sub(ptr_width) as *mut c_void; roc_host.dealloc(base, core::mem::align_of::()); } } @@ -716,28 +716,40 @@ pub struct Cmd { const _: () = assert!(core::mem::size_of::() == 80, "Cmd size mismatch"); const _: () = assert!(core::mem::align_of::() == 8, "Cmd alignment mismatch"); -/// Element type for __AnonStruct10 +/// Element type for __AnonStruct9 #[repr(C)] #[derive(Clone, Copy)] -pub struct AnonStruct10 { - pub stderr_utf8_lossy: RocStr, - pub stdout_utf8_lossy: RocStr, +pub struct AnonStruct9 { + pub stderr_bytes: RocListWith, + pub stdout_bytes: RocListWith, pub exit_code: i32, } -const _: () = assert!(core::mem::size_of::() == 56, "AnonStruct10 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct10 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 56, "AnonStruct9 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct9 alignment mismatch"); -/// Element type for __AnonStruct11 +/// Element type for __AnonStruct12 #[repr(C)] #[derive(Clone, Copy)] -pub struct AnonStruct11 { - pub stderr_utf8_lossy: RocStr, - pub stdout_utf8: RocStr, +pub struct AnonStruct12 { + pub stderr_bytes: RocListWith, + pub stdout_bytes: RocListWith, } -const _: () = assert!(core::mem::size_of::() == 48, "AnonStruct11 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct11 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 48, "AnonStruct12 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct12 alignment mismatch"); + +/// Element type for __AnonStruct44 +#[repr(C)] +#[derive(Clone, Copy)] +pub struct AnonStruct44 { + pub is_dir: bool, + pub is_file: bool, + pub is_sym_link: bool, +} + +const _: () = assert!(core::mem::size_of::() == 3, "AnonStruct44 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 1, "AnonStruct44 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] @@ -758,17 +770,17 @@ pub struct TryType0 { #[repr(C)] #[derive(Clone, Copy)] pub union TryType0Payload { - pub err: core::mem::ManuallyDrop, + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop, } const _: () = assert!(core::mem::size_of::() == 40, "TryType0 size mismatch"); const _: () = assert!(core::mem::align_of::() == 8, "TryType0 alignment mismatch"); -/// Tag discriminant for Cmd.IOErr. +/// Tag discriminant for IOErr. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CmdIOErrTag { +pub enum IOErrType1Tag { AlreadyExists = 0, BrokenPipe = 1, Interrupted = 2, @@ -779,17 +791,17 @@ pub enum CmdIOErrTag { Unsupported = 7, } -/// Tag union: Cmd.IOErr +/// Tag union: IOErr #[repr(C)] #[derive(Clone, Copy)] -pub struct CmdIOErr { - pub payload: CmdIOErrPayload, - pub tag: CmdIOErrTag, +pub struct IOErrType1 { + pub payload: IOErrType1Payload, + pub tag: IOErrType1Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union CmdIOErrPayload { +pub union IOErrType1Payload { pub already_exists: [u8; 0], pub broken_pipe: [u8; 0], pub interrupted: [u8; 0], @@ -800,13 +812,13 @@ pub union CmdIOErrPayload { pub unsupported: [u8; 0], } -const _: () = assert!(core::mem::size_of::() == 32, "CmdIOErr size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "CmdIOErr alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType1 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType1 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType8Tag { +pub enum TryType7Tag { Err = 0, Ok = 1, } @@ -814,51 +826,51 @@ pub enum TryType8Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType8 { - pub payload: TryType8Payload, - pub tag: TryType8Tag, +pub struct TryType7 { + pub payload: TryType7Payload, + pub tag: TryType7Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType8Payload { - pub err: core::mem::ManuallyDrop, - pub ok: core::mem::ManuallyDrop, +pub union TryType7Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop, } -const _: () = assert!(core::mem::size_of::() == 72, "TryType8 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType8 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 72, "TryType7 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType7 alignment mismatch"); -/// Tag discriminant for CmdErrOrNonZeroExit. +/// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CmdErrOrNonZeroExitTag { - CmdErr = 0, - NonZeroExit = 1, +pub enum TryType8Tag { + Err = 0, + Ok = 1, } -/// Tag union: CmdErrOrNonZeroExit +/// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct CmdErrOrNonZeroExit { - pub payload: CmdErrOrNonZeroExitPayload, - pub tag: CmdErrOrNonZeroExitTag, +pub struct TryType8 { + pub payload: TryType8Payload, + pub tag: TryType8Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union CmdErrOrNonZeroExitPayload { - pub cmd_err: core::mem::ManuallyDrop, - pub non_zero_exit: core::mem::ManuallyDrop, +pub union TryType8Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop, } -const _: () = assert!(core::mem::size_of::() == 64, "CmdErrOrNonZeroExit size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "CmdErrOrNonZeroExit alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 64, "TryType8 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType8 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType12Tag { +pub enum TryType13Tag { Err = 0, Ok = 1, } @@ -866,59 +878,63 @@ pub enum TryType12Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType12 { - pub payload: TryType12Payload, - pub tag: TryType12Tag, +pub struct TryType13 { + pub payload: TryType13Payload, + pub tag: TryType13Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType12Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType13Payload { + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop<()>, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType12 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType12 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType13 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType13 alignment mismatch"); -/// Tag discriminant for Dir.IOErr. +/// Tag discriminant for IOErr. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DirIOErrTag { +pub enum IOErrType15Tag { AlreadyExists = 0, - NotADirectory = 1, - NotEmpty = 2, + BrokenPipe = 1, + Interrupted = 2, NotFound = 3, Other = 4, - PermissionDenied = 5, + OutOfMemory = 5, + PermissionDenied = 6, + Unsupported = 7, } -/// Tag union: Dir.IOErr +/// Tag union: IOErr #[repr(C)] #[derive(Clone, Copy)] -pub struct DirIOErr { - pub payload: DirIOErrPayload, - pub tag: DirIOErrTag, +pub struct IOErrType15 { + pub payload: IOErrType15Payload, + pub tag: IOErrType15Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union DirIOErrPayload { +pub union IOErrType15Payload { pub already_exists: [u8; 0], - pub not_adirectory: [u8; 0], - pub not_empty: [u8; 0], + pub broken_pipe: [u8; 0], + pub interrupted: [u8; 0], pub not_found: [u8; 0], pub other: core::mem::ManuallyDrop, + pub out_of_memory: [u8; 0], pub permission_denied: [u8; 0], + pub unsupported: [u8; 0], } -const _: () = assert!(core::mem::size_of::() == 32, "DirIOErr size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "DirIOErr alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType15 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType15 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType17Tag { +pub enum TryType18Tag { Err = 0, Ok = 1, } @@ -926,25 +942,103 @@ pub enum TryType17Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType17 { - pub payload: TryType17Payload, - pub tag: TryType17Tag, +pub struct TryType18 { + pub payload: TryType18Payload, + pub tag: TryType18Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType17Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType18Payload { + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop>, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType17 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType17 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType18 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType18 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType20Tag { + Err = 0, + Ok = 1, +} + +/// Tag union: Try +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TryType20 { + pub payload: TryType20Payload, + pub tag: TryType20Tag, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TryType20Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop, +} + +const _: () = assert!(core::mem::size_of::() == 32, "TryType20 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType20 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType23Tag { + Err = 0, + Ok = 1, +} + +/// Tag union: Try +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TryType23 { + pub payload: TryType23Payload, + pub tag: TryType23Tag, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TryType23Payload { + pub err: core::mem::ManuallyDrop<*mut c_void>, + pub ok: core::mem::ManuallyDrop, +} + +const _: () = assert!(core::mem::size_of::() == 32, "TryType23 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType23 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType26Tag { + Err = 0, + Ok = 1, +} + +/// Tag union: Try +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TryType26 { + pub payload: TryType26Payload, + pub tag: TryType26Tag, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TryType26Payload { + pub err: core::mem::ManuallyDrop<*mut c_void>, + pub ok: core::mem::ManuallyDrop, +} + +const _: () = assert!(core::mem::size_of::() == 32, "TryType26 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType26 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType21Tag { +pub enum TryType28Tag { Err = 0, Ok = 1, } @@ -952,25 +1046,25 @@ pub enum TryType21Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType21 { - pub payload: TryType21Payload, - pub tag: TryType21Tag, +pub struct TryType28 { + pub payload: TryType28Payload, + pub tag: TryType28Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType21Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType28Payload { + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop>, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType21 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType21 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType28 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType28 alignment mismatch"); -/// Tag discriminant for File.IOErr. +/// Tag discriminant for IOErr. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FileIOErrTag { +pub enum IOErrType30Tag { AlreadyExists = 0, BrokenPipe = 1, Interrupted = 2, @@ -981,17 +1075,17 @@ pub enum FileIOErrTag { Unsupported = 7, } -/// Tag union: File.IOErr +/// Tag union: IOErr #[repr(C)] #[derive(Clone, Copy)] -pub struct FileIOErr { - pub payload: FileIOErrPayload, - pub tag: FileIOErrTag, +pub struct IOErrType30 { + pub payload: IOErrType30Payload, + pub tag: IOErrType30Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union FileIOErrPayload { +pub union IOErrType30Payload { pub already_exists: [u8; 0], pub broken_pipe: [u8; 0], pub interrupted: [u8; 0], @@ -1002,13 +1096,13 @@ pub union FileIOErrPayload { pub unsupported: [u8; 0], } -const _: () = assert!(core::mem::size_of::() == 32, "FileIOErr size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "FileIOErr alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType30 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType30 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType27Tag { +pub enum TryType34Tag { Err = 0, Ok = 1, } @@ -1016,25 +1110,51 @@ pub enum TryType27Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType27 { - pub payload: TryType27Payload, - pub tag: TryType27Tag, +pub struct TryType34 { + pub payload: TryType34Payload, + pub tag: TryType34Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType27Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType34Payload { + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop<()>, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType27 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType27 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType34 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType34 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType36Tag { + Err = 0, + Ok = 1, +} + +/// Tag union: Try +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TryType36 { + pub payload: TryType36Payload, + pub tag: TryType36Tag, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TryType36Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop, +} + +const _: () = assert!(core::mem::size_of::() == 40, "TryType36 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType36 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType29Tag { +pub enum TryType37Tag { Err = 0, Ok = 1, } @@ -1042,25 +1162,25 @@ pub enum TryType29Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType29 { - pub payload: TryType29Payload, - pub tag: TryType29Tag, +pub struct TryType37 { + pub payload: TryType37Payload, + pub tag: TryType37Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType29Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType37Payload { + pub err: core::mem::ManuallyDrop<*mut c_void>, pub ok: core::mem::ManuallyDrop, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType29 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType29 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 32, "TryType37 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType37 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType32Tag { +pub enum TryType41Tag { Err = 0, Ok = 1, } @@ -1068,25 +1188,25 @@ pub enum TryType32Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType32 { - pub payload: TryType32Payload, - pub tag: TryType32Tag, +pub struct TryType41 { + pub payload: TryType41Payload, + pub tag: TryType41Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType32Payload { - pub err: core::mem::ManuallyDrop, - pub ok: core::mem::ManuallyDrop, +pub union TryType41Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType32 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType32 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType41 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType41 alignment mismatch"); -/// Tag discriminant for Path.IOErr. +/// Tag discriminant for IOErr. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PathIOErrTag { +pub enum IOErrType42Tag { AlreadyExists = 0, BrokenPipe = 1, Interrupted = 2, @@ -1097,17 +1217,17 @@ pub enum PathIOErrTag { Unsupported = 7, } -/// Tag union: Path.IOErr +/// Tag union: IOErr #[repr(C)] #[derive(Clone, Copy)] -pub struct PathIOErr { - pub payload: PathIOErrPayload, - pub tag: PathIOErrTag, +pub struct IOErrType42 { + pub payload: IOErrType42Payload, + pub tag: IOErrType42Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union PathIOErrPayload { +pub union IOErrType42Payload { pub already_exists: [u8; 0], pub broken_pipe: [u8; 0], pub interrupted: [u8; 0], @@ -1118,13 +1238,13 @@ pub union PathIOErrPayload { pub unsupported: [u8; 0], } -const _: () = assert!(core::mem::size_of::() == 32, "PathIOErr size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "PathIOErr alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType42 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType42 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType37Tag { +pub enum TryType48Tag { Err = 0, Ok = 1, } @@ -1132,25 +1252,25 @@ pub enum TryType37Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType37 { - pub payload: TryType37Payload, - pub tag: TryType37Tag, +pub struct TryType48 { + pub payload: TryType48Payload, + pub tag: TryType48Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType37Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType48Payload { + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType37 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType37 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType48 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType48 alignment mismatch"); -/// Tag discriminant for Random.IOErr. +/// Tag discriminant for IOErr. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RandomIOErrTag { +pub enum IOErrType50Tag { AlreadyExists = 0, BrokenPipe = 1, Interrupted = 2, @@ -1161,17 +1281,17 @@ pub enum RandomIOErrTag { Unsupported = 7, } -/// Tag union: Random.IOErr +/// Tag union: IOErr #[repr(C)] #[derive(Clone, Copy)] -pub struct RandomIOErr { - pub payload: RandomIOErrPayload, - pub tag: RandomIOErrTag, +pub struct IOErrType50 { + pub payload: IOErrType50Payload, + pub tag: IOErrType50Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union RandomIOErrPayload { +pub union IOErrType50Payload { pub already_exists: [u8; 0], pub broken_pipe: [u8; 0], pub interrupted: [u8; 0], @@ -1182,13 +1302,13 @@ pub union RandomIOErrPayload { pub unsupported: [u8; 0], } -const _: () = assert!(core::mem::size_of::() == 32, "RandomIOErr size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "RandomIOErr alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType50 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType50 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType43Tag { +pub enum TryType54Tag { Err = 0, Ok = 1, } @@ -1196,25 +1316,25 @@ pub enum TryType43Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType43 { - pub payload: TryType43Payload, - pub tag: TryType43Tag, +pub struct TryType54 { + pub payload: TryType54Payload, + pub tag: TryType54Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType43Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType54Payload { + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop, } -const _: () = assert!(core::mem::size_of::() == 40, "TryType43 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "TryType43 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType54 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType54 alignment mismatch"); /// Tag discriminant for Try. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TryType59Tag { +pub enum TryType58Tag { Err = 0, Ok = 1, } @@ -1222,190 +1342,442 @@ pub enum TryType59Tag { /// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct TryType59 { - pub payload: TryType59Payload, - pub tag: TryType59Tag, +pub struct TryType58 { + pub payload: TryType58Payload, + pub tag: TryType58Tag, } #[repr(C)] #[derive(Clone, Copy)] -pub union TryType59Payload { - pub err: core::mem::ManuallyDrop, +pub union TryType58Payload { + pub err: core::mem::ManuallyDrop, pub ok: core::mem::ManuallyDrop<()>, } -const _: () = assert!(core::mem::size_of::() == 8, "TryType59 size mismatch"); -const _: () = assert!(core::mem::align_of::() == 4, "TryType59 alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 40, "TryType58 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType58 alignment mismatch"); -/// Arguments for Cmd.exec_exit_code! -/// Roc signature: Cmd => Try(I32, [CmdErr(Cmd.IOErr)]) -/// Refcounted fields are owned by the hosted function. +/// Tag discriminant for IOErr. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IOErrType60Tag { + AlreadyExists = 0, + BrokenPipe = 1, + Interrupted = 2, + NotFound = 3, + Other = 4, + OutOfMemory = 5, + PermissionDenied = 6, + Unsupported = 7, +} + +/// Tag union: IOErr #[repr(C)] #[derive(Clone, Copy)] -pub struct CmdExecExitCodeArgs { - pub args: RocList, - pub envs: RocList, - pub program: RocStr, - pub clear_envs: bool, +pub struct IOErrType60 { + pub payload: IOErrType60Payload, + pub tag: IOErrType60Tag, } -const _: () = assert!(core::mem::size_of::() == 80, "CmdExecExitCodeArgs size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "CmdExecExitCodeArgs alignment mismatch"); - -/// Arguments for Cmd.exec_output! -/// Roc signature: Cmd => Try({ stderr_utf8_lossy : Str, stdout_utf8 : Str }, [CmdErr(Cmd.IOErr), NonZeroExit({ exit_code : I32, stderr_utf8_lossy : Str, stdout_utf8_lossy : Str })]) -/// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] -pub struct CmdExecOutputArgs { - pub args: RocList, - pub envs: RocList, - pub program: RocStr, - pub clear_envs: bool, +pub union IOErrType60Payload { + pub already_exists: [u8; 0], + pub broken_pipe: [u8; 0], + pub interrupted: [u8; 0], + pub not_found: [u8; 0], + pub other: core::mem::ManuallyDrop, + pub out_of_memory: [u8; 0], + pub permission_denied: [u8; 0], + pub unsupported: [u8; 0], } -const _: () = assert!(core::mem::size_of::() == 80, "CmdExecOutputArgs size mismatch"); -const _: () = assert!(core::mem::align_of::() == 8, "CmdExecOutputArgs alignment mismatch"); +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType60 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType60 alignment mismatch"); -/// Arguments for Dir.create! -/// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) -/// Refcounted fields are owned by the hosted function. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct DirCreateArgs { - pub arg0: RocStr, +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType65Tag { + Err = 0, + Ok = 1, } -/// Arguments for Dir.create_all! -/// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) -/// Refcounted fields are owned by the hosted function. +/// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct DirCreateAllArgs { - pub arg0: RocStr, +pub struct TryType65 { + pub payload: TryType65Payload, + pub tag: TryType65Tag, } -/// Arguments for Dir.delete_all! -/// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) -/// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] -pub struct DirDeleteAllArgs { - pub arg0: RocStr, +pub union TryType65Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop, } -/// Arguments for Dir.delete_empty! -/// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) -/// Refcounted fields are owned by the hosted function. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct DirDeleteEmptyArgs { - pub arg0: RocStr, +const _: () = assert!(core::mem::size_of::() == 48, "TryType65 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType65 alignment mismatch"); + +/// Tag discriminant for EndOfFileOrStdinErr. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EndOfFileOrStdinErrTag { + EndOfFile = 0, + StdinErr = 1, } -/// Arguments for Dir.list! -/// Roc signature: Str => Try(List(Str), [DirErr(Dir.IOErr)]) -/// Refcounted fields are owned by the hosted function. +/// Tag union: EndOfFileOrStdinErr #[repr(C)] #[derive(Clone, Copy)] -pub struct DirListArgs { - pub arg0: RocStr, +pub struct EndOfFileOrStdinErr { + pub payload: EndOfFileOrStdinErrPayload, + pub tag: EndOfFileOrStdinErrTag, } -/// Arguments for Env.var! -/// Roc signature: Str => Str -/// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] -pub struct EnvVarArgs { - pub arg0: RocStr, +pub union EndOfFileOrStdinErrPayload { + pub end_of_file: [u8; 0], + pub stdin_err: core::mem::ManuallyDrop, } -/// Arguments for File.delete! -/// Roc signature: Str => Try({}, [FileErr(File.IOErr)]) -/// Refcounted fields are owned by the hosted function. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct FileDeleteArgs { - pub arg0: RocStr, +const _: () = assert!(core::mem::size_of::() == 40, "EndOfFileOrStdinErr size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "EndOfFileOrStdinErr alignment mismatch"); + +/// Tag discriminant for IOErr. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IOErrType67Tag { + AlreadyExists = 0, + BrokenPipe = 1, + Interrupted = 2, + NotFound = 3, + Other = 4, + OutOfMemory = 5, + PermissionDenied = 6, + Unsupported = 7, } -/// Arguments for File.read_bytes! -/// Roc signature: Str => Try(List(U8), [FileErr(File.IOErr)]) -/// Refcounted fields are owned by the hosted function. +/// Tag union: IOErr #[repr(C)] #[derive(Clone, Copy)] -pub struct FileReadBytesArgs { - pub arg0: RocStr, +pub struct IOErrType67 { + pub payload: IOErrType67Payload, + pub tag: IOErrType67Tag, } -/// Arguments for File.read_utf8! -/// Roc signature: Str => Try(Str, [FileErr(File.IOErr)]) -/// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] -pub struct FileReadUtf8Args { - pub arg0: RocStr, +pub union IOErrType67Payload { + pub already_exists: [u8; 0], + pub broken_pipe: [u8; 0], + pub interrupted: [u8; 0], + pub not_found: [u8; 0], + pub other: core::mem::ManuallyDrop, + pub out_of_memory: [u8; 0], + pub permission_denied: [u8; 0], + pub unsupported: [u8; 0], } -/// Arguments for File.write_bytes! -/// Roc signature: Str, List(U8) => Try({}, [FileErr(File.IOErr)]) -/// Refcounted fields are owned by the hosted function. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct FileWriteBytesArgs { - pub arg0: RocStr, - pub arg1: RocListWith, +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType67 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType67 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType70Tag { + Err = 0, + Ok = 1, } -/// Arguments for File.write_utf8! -/// Roc signature: Str, Str => Try({}, [FileErr(File.IOErr)]) -/// Refcounted fields are owned by the hosted function. +/// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct FileWriteUtf8Args { - pub arg0: RocStr, - pub arg1: RocStr, +pub struct TryType70 { + pub payload: TryType70Payload, + pub tag: TryType70Tag, } -/// Arguments for Path.is_dir! -/// Roc signature: Str => Try(Bool, [PathErr(Path.IOErr)]) -/// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] -pub struct PathIsDirArgs { - pub arg0: RocStr, +pub union TryType70Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop>, } -/// Arguments for Path.is_file! -/// Roc signature: Str => Try(Bool, [PathErr(Path.IOErr)]) -/// Refcounted fields are owned by the hosted function. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct PathIsFileArgs { - pub arg0: RocStr, +const _: () = assert!(core::mem::size_of::() == 48, "TryType70 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType70 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType73Tag { + Err = 0, + Ok = 1, } -/// Arguments for Path.is_sym_link! -/// Roc signature: Str => Try(Bool, [PathErr(Path.IOErr)]) -/// Refcounted fields are owned by the hosted function. +/// Tag union: Try #[repr(C)] #[derive(Clone, Copy)] -pub struct PathIsSymLinkArgs { - pub arg0: RocStr, +pub struct TryType73 { + pub payload: TryType73Payload, + pub tag: TryType73Tag, } -/// Arguments for Sleep.millis! -/// Roc signature: U64 => {} -/// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] -pub struct SleepMillisArgs { +pub union TryType73Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop>, +} + +const _: () = assert!(core::mem::size_of::() == 40, "TryType73 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType73 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType75Tag { + Err = 0, + Ok = 1, +} + +/// Tag union: Try +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TryType75 { + pub payload: TryType75Payload, + pub tag: TryType75Tag, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TryType75Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop<()>, +} + +const _: () = assert!(core::mem::size_of::() == 40, "TryType75 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "TryType75 alignment mismatch"); + +/// Tag discriminant for IOErr. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IOErrType77Tag { + AlreadyExists = 0, + BrokenPipe = 1, + Interrupted = 2, + NotFound = 3, + Other = 4, + OutOfMemory = 5, + PermissionDenied = 6, + Unsupported = 7, +} + +/// Tag union: IOErr +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IOErrType77 { + pub payload: IOErrType77Payload, + pub tag: IOErrType77Tag, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IOErrType77Payload { + pub already_exists: [u8; 0], + pub broken_pipe: [u8; 0], + pub interrupted: [u8; 0], + pub not_found: [u8; 0], + pub other: core::mem::ManuallyDrop, + pub out_of_memory: [u8; 0], + pub permission_denied: [u8; 0], + pub unsupported: [u8; 0], +} + +const _: () = assert!(core::mem::size_of::() == 32, "IOErrType77 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "IOErrType77 alignment mismatch"); + +/// Tag discriminant for Try. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryType88Tag { + Err = 0, + Ok = 1, +} + +/// Tag union: Try +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TryType88 { + pub payload: TryType88Payload, + pub tag: TryType88Tag, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union TryType88Payload { + pub err: core::mem::ManuallyDrop, + pub ok: core::mem::ManuallyDrop<()>, +} + +const _: () = assert!(core::mem::size_of::() == 8, "TryType88 size mismatch"); +const _: () = assert!(core::mem::align_of::() == 4, "TryType88 alignment mismatch"); + +/// Arguments for Cmd.host_exec_exit_code! +/// Roc signature: Cmd => Try(I32, IOErr) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CmdHostExecExitCodeArgs { + pub args: RocList, + pub envs: RocList, + pub program: RocStr, + pub clear_envs: bool, +} + +const _: () = assert!(core::mem::size_of::() == 80, "CmdHostExecExitCodeArgs size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "CmdHostExecExitCodeArgs alignment mismatch"); + +/// Arguments for Cmd.host_exec_output! +/// Roc signature: Cmd => Try({ stderr_bytes : List(U8), stdout_bytes : List(U8) }, Try({ exit_code : I32, stderr_bytes : List(U8), stdout_bytes : List(U8) }, IOErr)) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CmdHostExecOutputArgs { + pub args: RocList, + pub envs: RocList, + pub program: RocStr, + pub clear_envs: bool, +} + +const _: () = assert!(core::mem::size_of::() == 80, "CmdHostExecOutputArgs size mismatch"); +const _: () = assert!(core::mem::align_of::() == 8, "CmdHostExecOutputArgs alignment mismatch"); + +/// Arguments for Dir.create! +/// Roc signature: Str => Try({}, [DirErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct DirCreateArgs { + pub arg0: RocStr, +} + +/// Arguments for Dir.create_all! +/// Roc signature: Str => Try({}, [DirErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct DirCreateAllArgs { + pub arg0: RocStr, +} + +/// Arguments for Dir.delete_all! +/// Roc signature: Str => Try({}, [DirErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct DirDeleteAllArgs { + pub arg0: RocStr, +} + +/// Arguments for Dir.delete_empty! +/// Roc signature: Str => Try({}, [DirErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct DirDeleteEmptyArgs { + pub arg0: RocStr, +} + +/// Arguments for Dir.list! +/// Roc signature: Str => Try(List(Str), [DirErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct DirListArgs { + pub arg0: RocStr, +} + +/// Arguments for Env.var! +/// Roc signature: Str => Try(Str, [VarNotFound(Str)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct EnvVarArgs { + pub arg0: RocStr, +} + +/// Arguments for File.delete! +/// Roc signature: Str => Try({}, [FileErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FileDeleteArgs { + pub arg0: RocStr, +} + +/// Arguments for File.read_bytes! +/// Roc signature: Str => Try(List(U8), [FileErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FileReadBytesArgs { + pub arg0: RocStr, +} + +/// Arguments for File.read_utf8! +/// Roc signature: Str => Try(Str, [FileErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FileReadUtf8Args { + pub arg0: RocStr, +} + +/// Arguments for File.write_bytes! +/// Roc signature: Str, List(U8) => Try({}, [FileErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FileWriteBytesArgs { + pub arg0: RocStr, + pub arg1: RocListWith, +} + +/// Arguments for File.write_utf8! +/// Roc signature: Str, Str => Try({}, [FileErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FileWriteUtf8Args { + pub arg0: RocStr, + pub arg1: RocStr, +} + +/// Arguments for Path.host_path_type! +/// Roc signature: List(U8) => Try({ is_dir : Bool, is_file : Bool, is_sym_link : Bool }, IOErr) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct PathHostPathTypeArgs { + pub arg0: RocListWith, +} + +/// Arguments for Sleep.millis! +/// Roc signature: U64 => {} +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SleepMillisArgs { pub arg0: u64, } /// Arguments for Stderr.line! -/// Roc signature: Str => {} +/// Roc signature: Str => Try({}, [StderrErr(IOErr)]) /// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] @@ -1414,7 +1786,7 @@ pub struct StderrLineArgs { } /// Arguments for Stderr.write! -/// Roc signature: Str => {} +/// Roc signature: Str => Try({}, [StderrErr(IOErr)]) /// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] @@ -1422,8 +1794,17 @@ pub struct StderrWriteArgs { pub arg0: RocStr, } +/// Arguments for Stderr.write_bytes! +/// Roc signature: List(U8) => Try({}, [StderrErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct StderrWriteBytesArgs { + pub arg0: RocListWith, +} + /// Arguments for Stdout.line! -/// Roc signature: Str => {} +/// Roc signature: Str => Try({}, [StdoutErr(IOErr)]) /// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] @@ -1432,7 +1813,7 @@ pub struct StdoutLineArgs { } /// Arguments for Stdout.write! -/// Roc signature: Str => {} +/// Roc signature: Str => Try({}, [StdoutErr(IOErr)]) /// Refcounted fields are owned by the hosted function. #[repr(C)] #[derive(Clone, Copy)] @@ -1440,6 +1821,15 @@ pub struct StdoutWriteArgs { pub arg0: RocStr, } +/// Arguments for Stdout.write_bytes! +/// Roc signature: List(U8) => Try({}, [StdoutErr(IOErr)]) +/// Refcounted fields are owned by the hosted function. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct StdoutWriteBytesArgs { + pub arg0: RocListWith, +} + // ============================================================================= // Generated Refcount Helpers // ============================================================================= @@ -1450,7 +1840,7 @@ pub fn decref_try_type0(value: TryType0, roc_host: &RocHost) { match value.tag { TryType0Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_cmd_ioerr(payload, roc_host); + decref_ioerr_type1(payload, roc_host); }, TryType0Tag::Ok => {}, } @@ -1462,45 +1852,45 @@ pub fn incref_try_type0(value: TryType0, amount: isize) { match value.tag { TryType0Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_cmd_ioerr(payload, amount); + incref_ioerr_type1(payload, amount); }, TryType0Tag::Ok => {}, } } -/// Recursively decrement Roc-owned payloads in CmdIOErr. -pub fn decref_cmd_ioerr(value: CmdIOErr, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in IOErrType1. +pub fn decref_ioerr_type1(value: IOErrType1, roc_host: &RocHost) { let _ = roc_host; match value.tag { - CmdIOErrTag::AlreadyExists => {}, - CmdIOErrTag::BrokenPipe => {}, - CmdIOErrTag::Interrupted => {}, - CmdIOErrTag::NotFound => {}, - CmdIOErrTag::Other => unsafe { + IOErrType1Tag::AlreadyExists => {}, + IOErrType1Tag::BrokenPipe => {}, + IOErrType1Tag::Interrupted => {}, + IOErrType1Tag::NotFound => {}, + IOErrType1Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.decref(roc_host); }, - CmdIOErrTag::OutOfMemory => {}, - CmdIOErrTag::PermissionDenied => {}, - CmdIOErrTag::Unsupported => {}, + IOErrType1Tag::OutOfMemory => {}, + IOErrType1Tag::PermissionDenied => {}, + IOErrType1Tag::Unsupported => {}, } } -/// Increment Roc-owned payloads in CmdIOErr. -pub fn incref_cmd_ioerr(value: CmdIOErr, amount: isize) { +/// Increment Roc-owned payloads in IOErrType1. +pub fn incref_ioerr_type1(value: IOErrType1, amount: isize) { let _ = amount; match value.tag { - CmdIOErrTag::AlreadyExists => {}, - CmdIOErrTag::BrokenPipe => {}, - CmdIOErrTag::Interrupted => {}, - CmdIOErrTag::NotFound => {}, - CmdIOErrTag::Other => unsafe { + IOErrType1Tag::AlreadyExists => {}, + IOErrType1Tag::BrokenPipe => {}, + IOErrType1Tag::Interrupted => {}, + IOErrType1Tag::NotFound => {}, + IOErrType1Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.incref(amount); }, - CmdIOErrTag::OutOfMemory => {}, - CmdIOErrTag::PermissionDenied => {}, - CmdIOErrTag::Unsupported => {}, + IOErrType1Tag::OutOfMemory => {}, + IOErrType1Tag::PermissionDenied => {}, + IOErrType1Tag::Unsupported => {}, } } @@ -1509,8 +1899,8 @@ pub fn decref_cmd(value: Cmd, roc_host: &RocHost) { { let list = value.args; if list.has_one_ref() { - for item in list.allocation_items().iter().copied() { - item.decref(roc_host); + for item in list.allocation_items() { + (*item).decref(roc_host); } } list.decref(roc_host); @@ -1518,8 +1908,8 @@ pub fn decref_cmd(value: Cmd, roc_host: &RocHost) { { let list = value.envs; if list.has_one_ref() { - for item in list.allocation_items().iter().copied() { - item.decref(roc_host); + for item in list.allocation_items() { + (*item).decref(roc_host); } } list.decref(roc_host); @@ -1534,161 +1924,177 @@ pub fn incref_cmd(value: Cmd, amount: isize) { value.program.incref(amount); } -/// Recursively decrement Roc-owned payloads in TryType8. -pub fn decref_try_type8(value: TryType8, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType7. +pub fn decref_try_type7(value: TryType7, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType8Tag::Err => unsafe { + TryType7Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_cmd_err_or_non_zero_exit(payload, roc_host); + decref_try_type8(payload, roc_host); }, - TryType8Tag::Ok => unsafe { + TryType7Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); - decref_anon_struct11(payload, roc_host); + decref_anon_struct12(payload, roc_host); }, } } -/// Increment Roc-owned payloads in TryType8. -pub fn incref_try_type8(value: TryType8, amount: isize) { +/// Increment Roc-owned payloads in TryType7. +pub fn incref_try_type7(value: TryType7, amount: isize) { let _ = amount; match value.tag { - TryType8Tag::Err => unsafe { + TryType7Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_cmd_err_or_non_zero_exit(payload, amount); + incref_try_type8(payload, amount); }, - TryType8Tag::Ok => unsafe { + TryType7Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); - incref_anon_struct11(payload, amount); + incref_anon_struct12(payload, amount); }, } } -/// Recursively decrement Roc-owned payloads in CmdErrOrNonZeroExit. -pub fn decref_cmd_err_or_non_zero_exit(value: CmdErrOrNonZeroExit, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType8. +pub fn decref_try_type8(value: TryType8, roc_host: &RocHost) { let _ = roc_host; match value.tag { - CmdErrOrNonZeroExitTag::CmdErr => unsafe { - let payload = core::mem::ManuallyDrop::into_inner(value.payload.cmd_err); - decref_cmd_ioerr(payload, roc_host); + TryType8Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + decref_ioerr_type1(payload, roc_host); }, - CmdErrOrNonZeroExitTag::NonZeroExit => unsafe { - let payload = core::mem::ManuallyDrop::into_inner(value.payload.non_zero_exit); - decref_anon_struct10(payload, roc_host); + TryType8Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + decref_anon_struct9(payload, roc_host); }, } } -/// Increment Roc-owned payloads in CmdErrOrNonZeroExit. -pub fn incref_cmd_err_or_non_zero_exit(value: CmdErrOrNonZeroExit, amount: isize) { +/// Increment Roc-owned payloads in TryType8. +pub fn incref_try_type8(value: TryType8, amount: isize) { let _ = amount; match value.tag { - CmdErrOrNonZeroExitTag::CmdErr => unsafe { - let payload = core::mem::ManuallyDrop::into_inner(value.payload.cmd_err); - incref_cmd_ioerr(payload, amount); + TryType8Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + incref_ioerr_type1(payload, amount); }, - CmdErrOrNonZeroExitTag::NonZeroExit => unsafe { - let payload = core::mem::ManuallyDrop::into_inner(value.payload.non_zero_exit); - incref_anon_struct10(payload, amount); + TryType8Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + incref_anon_struct9(payload, amount); }, } } -/// Recursively decrement Roc-owned fields in AnonStruct10. -pub fn decref_anon_struct10(value: AnonStruct10, roc_host: &RocHost) { - value.stderr_utf8_lossy.decref(roc_host); - value.stdout_utf8_lossy.decref(roc_host); +/// Recursively decrement Roc-owned fields in AnonStruct9. +pub fn decref_anon_struct9(value: AnonStruct9, roc_host: &RocHost) { + { + let list = value.stderr_bytes; + list.decref(roc_host); + } + { + let list = value.stdout_bytes; + list.decref(roc_host); + } } -/// Increment Roc-owned fields in AnonStruct10. -pub fn incref_anon_struct10(value: AnonStruct10, amount: isize) { - value.stderr_utf8_lossy.incref(amount); - value.stdout_utf8_lossy.incref(amount); +/// Increment Roc-owned fields in AnonStruct9. +pub fn incref_anon_struct9(value: AnonStruct9, amount: isize) { + value.stderr_bytes.incref(amount); + value.stdout_bytes.incref(amount); } -/// Recursively decrement Roc-owned fields in AnonStruct11. -pub fn decref_anon_struct11(value: AnonStruct11, roc_host: &RocHost) { - value.stderr_utf8_lossy.decref(roc_host); - value.stdout_utf8.decref(roc_host); +/// Recursively decrement Roc-owned fields in AnonStruct12. +pub fn decref_anon_struct12(value: AnonStruct12, roc_host: &RocHost) { + { + let list = value.stderr_bytes; + list.decref(roc_host); + } + { + let list = value.stdout_bytes; + list.decref(roc_host); + } } -/// Increment Roc-owned fields in AnonStruct11. -pub fn incref_anon_struct11(value: AnonStruct11, amount: isize) { - value.stderr_utf8_lossy.incref(amount); - value.stdout_utf8.incref(amount); +/// Increment Roc-owned fields in AnonStruct12. +pub fn incref_anon_struct12(value: AnonStruct12, amount: isize) { + value.stderr_bytes.incref(amount); + value.stdout_bytes.incref(amount); } -/// Recursively decrement Roc-owned payloads in TryType12. -pub fn decref_try_type12(value: TryType12, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType13. +pub fn decref_try_type13(value: TryType13, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType12Tag::Err => unsafe { + TryType13Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_dir_ioerr(payload, roc_host); + decref_ioerr_type15(payload, roc_host); }, - TryType12Tag::Ok => {}, + TryType13Tag::Ok => {}, } } -/// Increment Roc-owned payloads in TryType12. -pub fn incref_try_type12(value: TryType12, amount: isize) { +/// Increment Roc-owned payloads in TryType13. +pub fn incref_try_type13(value: TryType13, amount: isize) { let _ = amount; match value.tag { - TryType12Tag::Err => unsafe { + TryType13Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_dir_ioerr(payload, amount); + incref_ioerr_type15(payload, amount); }, - TryType12Tag::Ok => {}, + TryType13Tag::Ok => {}, } } -/// Recursively decrement Roc-owned payloads in DirIOErr. -pub fn decref_dir_ioerr(value: DirIOErr, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in IOErrType15. +pub fn decref_ioerr_type15(value: IOErrType15, roc_host: &RocHost) { let _ = roc_host; match value.tag { - DirIOErrTag::AlreadyExists => {}, - DirIOErrTag::NotADirectory => {}, - DirIOErrTag::NotEmpty => {}, - DirIOErrTag::NotFound => {}, - DirIOErrTag::Other => unsafe { + IOErrType15Tag::AlreadyExists => {}, + IOErrType15Tag::BrokenPipe => {}, + IOErrType15Tag::Interrupted => {}, + IOErrType15Tag::NotFound => {}, + IOErrType15Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.decref(roc_host); }, - DirIOErrTag::PermissionDenied => {}, + IOErrType15Tag::OutOfMemory => {}, + IOErrType15Tag::PermissionDenied => {}, + IOErrType15Tag::Unsupported => {}, } } -/// Increment Roc-owned payloads in DirIOErr. -pub fn incref_dir_ioerr(value: DirIOErr, amount: isize) { +/// Increment Roc-owned payloads in IOErrType15. +pub fn incref_ioerr_type15(value: IOErrType15, amount: isize) { let _ = amount; match value.tag { - DirIOErrTag::AlreadyExists => {}, - DirIOErrTag::NotADirectory => {}, - DirIOErrTag::NotEmpty => {}, - DirIOErrTag::NotFound => {}, - DirIOErrTag::Other => unsafe { + IOErrType15Tag::AlreadyExists => {}, + IOErrType15Tag::BrokenPipe => {}, + IOErrType15Tag::Interrupted => {}, + IOErrType15Tag::NotFound => {}, + IOErrType15Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.incref(amount); }, - DirIOErrTag::PermissionDenied => {}, + IOErrType15Tag::OutOfMemory => {}, + IOErrType15Tag::PermissionDenied => {}, + IOErrType15Tag::Unsupported => {}, } } -/// Recursively decrement Roc-owned payloads in TryType17. -pub fn decref_try_type17(value: TryType17, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType18. +pub fn decref_try_type18(value: TryType18, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType17Tag::Err => unsafe { + TryType18Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_dir_ioerr(payload, roc_host); + decref_ioerr_type15(payload, roc_host); }, - TryType17Tag::Ok => unsafe { + TryType18Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); { let list = payload; if list.has_one_ref() { - for item in list.allocation_items().iter().copied() { - item.decref(roc_host); + for item in list.allocation_items() { + (*item).decref(roc_host); } } list.decref(roc_host); @@ -1697,30 +2103,108 @@ pub fn decref_try_type17(value: TryType17, roc_host: &RocHost) { } } -/// Increment Roc-owned payloads in TryType17. -pub fn incref_try_type17(value: TryType17, amount: isize) { +/// Increment Roc-owned payloads in TryType18. +pub fn incref_try_type18(value: TryType18, amount: isize) { let _ = amount; match value.tag { - TryType17Tag::Err => unsafe { + TryType18Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_dir_ioerr(payload, amount); + incref_ioerr_type15(payload, amount); }, - TryType17Tag::Ok => unsafe { + TryType18Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); payload.incref(amount); }, } } -/// Recursively decrement Roc-owned payloads in TryType21. -pub fn decref_try_type21(value: TryType21, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType20. +pub fn decref_try_type20(value: TryType20, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType21Tag::Err => unsafe { + TryType20Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_file_ioerr(payload, roc_host); + payload.decref(roc_host); }, - TryType21Tag::Ok => unsafe { + TryType20Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.decref(roc_host); + }, + } +} + +/// Increment Roc-owned payloads in TryType20. +pub fn incref_try_type20(value: TryType20, amount: isize) { + let _ = amount; + match value.tag { + TryType20Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + payload.incref(amount); + }, + TryType20Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in TryType23. +pub fn decref_try_type23(value: TryType23, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType23Tag::Err => {}, + TryType23Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.decref(roc_host); + }, + } +} + +/// Increment Roc-owned payloads in TryType23. +pub fn incref_try_type23(value: TryType23, amount: isize) { + let _ = amount; + match value.tag { + TryType23Tag::Err => {}, + TryType23Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in TryType26. +pub fn decref_try_type26(value: TryType26, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType26Tag::Err => {}, + TryType26Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.decref(roc_host); + }, + } +} + +/// Increment Roc-owned payloads in TryType26. +pub fn incref_try_type26(value: TryType26, amount: isize) { + let _ = amount; + match value.tag { + TryType26Tag::Err => {}, + TryType26Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in TryType28. +pub fn decref_try_type28(value: TryType28, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType28Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + decref_ioerr_type30(payload, roc_host); + }, + TryType28Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); { let list = payload; @@ -1730,270 +2214,588 @@ pub fn decref_try_type21(value: TryType21, roc_host: &RocHost) { } } -/// Increment Roc-owned payloads in TryType21. -pub fn incref_try_type21(value: TryType21, amount: isize) { +/// Increment Roc-owned payloads in TryType28. +pub fn incref_try_type28(value: TryType28, amount: isize) { let _ = amount; match value.tag { - TryType21Tag::Err => unsafe { + TryType28Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_file_ioerr(payload, amount); + incref_ioerr_type30(payload, amount); }, - TryType21Tag::Ok => unsafe { + TryType28Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); payload.incref(amount); }, } } -/// Recursively decrement Roc-owned payloads in FileIOErr. -pub fn decref_file_ioerr(value: FileIOErr, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in IOErrType30. +pub fn decref_ioerr_type30(value: IOErrType30, roc_host: &RocHost) { let _ = roc_host; match value.tag { - FileIOErrTag::AlreadyExists => {}, - FileIOErrTag::BrokenPipe => {}, - FileIOErrTag::Interrupted => {}, - FileIOErrTag::NotFound => {}, - FileIOErrTag::Other => unsafe { + IOErrType30Tag::AlreadyExists => {}, + IOErrType30Tag::BrokenPipe => {}, + IOErrType30Tag::Interrupted => {}, + IOErrType30Tag::NotFound => {}, + IOErrType30Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.decref(roc_host); }, - FileIOErrTag::OutOfMemory => {}, - FileIOErrTag::PermissionDenied => {}, - FileIOErrTag::Unsupported => {}, + IOErrType30Tag::OutOfMemory => {}, + IOErrType30Tag::PermissionDenied => {}, + IOErrType30Tag::Unsupported => {}, } } -/// Increment Roc-owned payloads in FileIOErr. -pub fn incref_file_ioerr(value: FileIOErr, amount: isize) { +/// Increment Roc-owned payloads in IOErrType30. +pub fn incref_ioerr_type30(value: IOErrType30, amount: isize) { let _ = amount; match value.tag { - FileIOErrTag::AlreadyExists => {}, - FileIOErrTag::BrokenPipe => {}, - FileIOErrTag::Interrupted => {}, - FileIOErrTag::NotFound => {}, - FileIOErrTag::Other => unsafe { + IOErrType30Tag::AlreadyExists => {}, + IOErrType30Tag::BrokenPipe => {}, + IOErrType30Tag::Interrupted => {}, + IOErrType30Tag::NotFound => {}, + IOErrType30Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.incref(amount); }, - FileIOErrTag::OutOfMemory => {}, - FileIOErrTag::PermissionDenied => {}, - FileIOErrTag::Unsupported => {}, + IOErrType30Tag::OutOfMemory => {}, + IOErrType30Tag::PermissionDenied => {}, + IOErrType30Tag::Unsupported => {}, } } -/// Recursively decrement Roc-owned payloads in TryType27. -pub fn decref_try_type27(value: TryType27, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType34. +pub fn decref_try_type34(value: TryType34, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType27Tag::Err => unsafe { + TryType34Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_file_ioerr(payload, roc_host); + decref_ioerr_type30(payload, roc_host); }, - TryType27Tag::Ok => {}, + TryType34Tag::Ok => {}, } } -/// Increment Roc-owned payloads in TryType27. -pub fn incref_try_type27(value: TryType27, amount: isize) { +/// Increment Roc-owned payloads in TryType34. +pub fn incref_try_type34(value: TryType34, amount: isize) { let _ = amount; match value.tag { - TryType27Tag::Err => unsafe { + TryType34Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_file_ioerr(payload, amount); + incref_ioerr_type30(payload, amount); }, - TryType27Tag::Ok => {}, + TryType34Tag::Ok => {}, } } -/// Recursively decrement Roc-owned payloads in TryType29. -pub fn decref_try_type29(value: TryType29, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType36. +pub fn decref_try_type36(value: TryType36, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType29Tag::Err => unsafe { + TryType36Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_file_ioerr(payload, roc_host); + decref_ioerr_type30(payload, roc_host); + }, + TryType36Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.decref(roc_host); }, - TryType29Tag::Ok => unsafe { + } +} + +/// Increment Roc-owned payloads in TryType36. +pub fn incref_try_type36(value: TryType36, amount: isize) { + let _ = amount; + match value.tag { + TryType36Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + incref_ioerr_type30(payload, amount); + }, + TryType36Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in TryType37. +pub fn decref_try_type37(value: TryType37, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType37Tag::Err => {}, + TryType37Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); payload.decref(roc_host); }, } } -/// Increment Roc-owned payloads in TryType29. -pub fn incref_try_type29(value: TryType29, amount: isize) { +/// Increment Roc-owned payloads in TryType37. +pub fn incref_try_type37(value: TryType37, amount: isize) { + let _ = amount; + match value.tag { + TryType37Tag::Err => {}, + TryType37Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in TryType41. +pub fn decref_try_type41(value: TryType41, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType41Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + decref_ioerr_type42(payload, roc_host); + }, + TryType41Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + decref_anon_struct44(payload, roc_host); + }, + } +} + +/// Increment Roc-owned payloads in TryType41. +pub fn incref_try_type41(value: TryType41, amount: isize) { let _ = amount; match value.tag { - TryType29Tag::Err => unsafe { + TryType41Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_file_ioerr(payload, amount); + incref_ioerr_type42(payload, amount); }, - TryType29Tag::Ok => unsafe { + TryType41Tag::Ok => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + incref_anon_struct44(payload, amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in IOErrType42. +pub fn decref_ioerr_type42(value: IOErrType42, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + IOErrType42Tag::AlreadyExists => {}, + IOErrType42Tag::BrokenPipe => {}, + IOErrType42Tag::Interrupted => {}, + IOErrType42Tag::NotFound => {}, + IOErrType42Tag::Other => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); + payload.decref(roc_host); + }, + IOErrType42Tag::OutOfMemory => {}, + IOErrType42Tag::PermissionDenied => {}, + IOErrType42Tag::Unsupported => {}, + } +} + +/// Increment Roc-owned payloads in IOErrType42. +pub fn incref_ioerr_type42(value: IOErrType42, amount: isize) { + let _ = amount; + match value.tag { + IOErrType42Tag::AlreadyExists => {}, + IOErrType42Tag::BrokenPipe => {}, + IOErrType42Tag::Interrupted => {}, + IOErrType42Tag::NotFound => {}, + IOErrType42Tag::Other => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.incref(amount); }, + IOErrType42Tag::OutOfMemory => {}, + IOErrType42Tag::PermissionDenied => {}, + IOErrType42Tag::Unsupported => {}, } } -/// Recursively decrement Roc-owned payloads in TryType32. -pub fn decref_try_type32(value: TryType32, roc_host: &RocHost) { +/// Recursively decrement Roc-owned fields in AnonStruct44. +pub fn decref_anon_struct44(value: AnonStruct44, roc_host: &RocHost) { + let _ = value; + let _ = roc_host; +} + +/// Increment Roc-owned fields in AnonStruct44. +pub fn incref_anon_struct44(value: AnonStruct44, amount: isize) { + let _ = value; + let _ = amount; +} + +/// Recursively decrement Roc-owned payloads in TryType48. +pub fn decref_try_type48(value: TryType48, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType32Tag::Err => unsafe { + TryType48Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_path_ioerr(payload, roc_host); + decref_ioerr_type50(payload, roc_host); }, - TryType32Tag::Ok => {}, + TryType48Tag::Ok => {}, } } -/// Increment Roc-owned payloads in TryType32. -pub fn incref_try_type32(value: TryType32, amount: isize) { +/// Increment Roc-owned payloads in TryType48. +pub fn incref_try_type48(value: TryType48, amount: isize) { let _ = amount; match value.tag { - TryType32Tag::Err => unsafe { + TryType48Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_path_ioerr(payload, amount); + incref_ioerr_type50(payload, amount); }, - TryType32Tag::Ok => {}, + TryType48Tag::Ok => {}, } } -/// Recursively decrement Roc-owned payloads in PathIOErr. -pub fn decref_path_ioerr(value: PathIOErr, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in IOErrType50. +pub fn decref_ioerr_type50(value: IOErrType50, roc_host: &RocHost) { let _ = roc_host; match value.tag { - PathIOErrTag::AlreadyExists => {}, - PathIOErrTag::BrokenPipe => {}, - PathIOErrTag::Interrupted => {}, - PathIOErrTag::NotFound => {}, - PathIOErrTag::Other => unsafe { + IOErrType50Tag::AlreadyExists => {}, + IOErrType50Tag::BrokenPipe => {}, + IOErrType50Tag::Interrupted => {}, + IOErrType50Tag::NotFound => {}, + IOErrType50Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.decref(roc_host); }, - PathIOErrTag::OutOfMemory => {}, - PathIOErrTag::PermissionDenied => {}, - PathIOErrTag::Unsupported => {}, + IOErrType50Tag::OutOfMemory => {}, + IOErrType50Tag::PermissionDenied => {}, + IOErrType50Tag::Unsupported => {}, } } -/// Increment Roc-owned payloads in PathIOErr. -pub fn incref_path_ioerr(value: PathIOErr, amount: isize) { +/// Increment Roc-owned payloads in IOErrType50. +pub fn incref_ioerr_type50(value: IOErrType50, amount: isize) { let _ = amount; match value.tag { - PathIOErrTag::AlreadyExists => {}, - PathIOErrTag::BrokenPipe => {}, - PathIOErrTag::Interrupted => {}, - PathIOErrTag::NotFound => {}, - PathIOErrTag::Other => unsafe { + IOErrType50Tag::AlreadyExists => {}, + IOErrType50Tag::BrokenPipe => {}, + IOErrType50Tag::Interrupted => {}, + IOErrType50Tag::NotFound => {}, + IOErrType50Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.incref(amount); }, - PathIOErrTag::OutOfMemory => {}, - PathIOErrTag::PermissionDenied => {}, - PathIOErrTag::Unsupported => {}, + IOErrType50Tag::OutOfMemory => {}, + IOErrType50Tag::PermissionDenied => {}, + IOErrType50Tag::Unsupported => {}, } } -/// Recursively decrement Roc-owned payloads in TryType37. -pub fn decref_try_type37(value: TryType37, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType54. +pub fn decref_try_type54(value: TryType54, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType37Tag::Err => unsafe { + TryType54Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_random_ioerr(payload, roc_host); + decref_ioerr_type50(payload, roc_host); }, - TryType37Tag::Ok => {}, + TryType54Tag::Ok => {}, } } -/// Increment Roc-owned payloads in TryType37. -pub fn incref_try_type37(value: TryType37, amount: isize) { +/// Increment Roc-owned payloads in TryType54. +pub fn incref_try_type54(value: TryType54, amount: isize) { + let _ = amount; + match value.tag { + TryType54Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + incref_ioerr_type50(payload, amount); + }, + TryType54Tag::Ok => {}, + } +} + +/// Recursively decrement Roc-owned payloads in TryType58. +pub fn decref_try_type58(value: TryType58, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType58Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + decref_ioerr_type60(payload, roc_host); + }, + TryType58Tag::Ok => {}, + } +} + +/// Increment Roc-owned payloads in TryType58. +pub fn incref_try_type58(value: TryType58, amount: isize) { + let _ = amount; + match value.tag { + TryType58Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + incref_ioerr_type60(payload, amount); + }, + TryType58Tag::Ok => {}, + } +} + +/// Recursively decrement Roc-owned payloads in IOErrType60. +pub fn decref_ioerr_type60(value: IOErrType60, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + IOErrType60Tag::AlreadyExists => {}, + IOErrType60Tag::BrokenPipe => {}, + IOErrType60Tag::Interrupted => {}, + IOErrType60Tag::NotFound => {}, + IOErrType60Tag::Other => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); + payload.decref(roc_host); + }, + IOErrType60Tag::OutOfMemory => {}, + IOErrType60Tag::PermissionDenied => {}, + IOErrType60Tag::Unsupported => {}, + } +} + +/// Increment Roc-owned payloads in IOErrType60. +pub fn incref_ioerr_type60(value: IOErrType60, amount: isize) { + let _ = amount; + match value.tag { + IOErrType60Tag::AlreadyExists => {}, + IOErrType60Tag::BrokenPipe => {}, + IOErrType60Tag::Interrupted => {}, + IOErrType60Tag::NotFound => {}, + IOErrType60Tag::Other => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); + payload.incref(amount); + }, + IOErrType60Tag::OutOfMemory => {}, + IOErrType60Tag::PermissionDenied => {}, + IOErrType60Tag::Unsupported => {}, + } +} + +/// Recursively decrement Roc-owned payloads in TryType65. +pub fn decref_try_type65(value: TryType65, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType65Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + decref_end_of_file_or_stdin_err(payload, roc_host); + }, + TryType65Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.decref(roc_host); + }, + } +} + +/// Increment Roc-owned payloads in TryType65. +pub fn incref_try_type65(value: TryType65, amount: isize) { let _ = amount; match value.tag { - TryType37Tag::Err => unsafe { + TryType65Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_random_ioerr(payload, amount); + incref_end_of_file_or_stdin_err(payload, amount); + }, + TryType65Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in EndOfFileOrStdinErr. +pub fn decref_end_of_file_or_stdin_err(value: EndOfFileOrStdinErr, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + EndOfFileOrStdinErrTag::EndOfFile => {}, + EndOfFileOrStdinErrTag::StdinErr => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.stdin_err); + decref_ioerr_type67(payload, roc_host); + }, + } +} + +/// Increment Roc-owned payloads in EndOfFileOrStdinErr. +pub fn incref_end_of_file_or_stdin_err(value: EndOfFileOrStdinErr, amount: isize) { + let _ = amount; + match value.tag { + EndOfFileOrStdinErrTag::EndOfFile => {}, + EndOfFileOrStdinErrTag::StdinErr => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.stdin_err); + incref_ioerr_type67(payload, amount); }, - TryType37Tag::Ok => {}, } } -/// Recursively decrement Roc-owned payloads in RandomIOErr. -pub fn decref_random_ioerr(value: RandomIOErr, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in IOErrType67. +pub fn decref_ioerr_type67(value: IOErrType67, roc_host: &RocHost) { let _ = roc_host; match value.tag { - RandomIOErrTag::AlreadyExists => {}, - RandomIOErrTag::BrokenPipe => {}, - RandomIOErrTag::Interrupted => {}, - RandomIOErrTag::NotFound => {}, - RandomIOErrTag::Other => unsafe { + IOErrType67Tag::AlreadyExists => {}, + IOErrType67Tag::BrokenPipe => {}, + IOErrType67Tag::Interrupted => {}, + IOErrType67Tag::NotFound => {}, + IOErrType67Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.decref(roc_host); }, - RandomIOErrTag::OutOfMemory => {}, - RandomIOErrTag::PermissionDenied => {}, - RandomIOErrTag::Unsupported => {}, + IOErrType67Tag::OutOfMemory => {}, + IOErrType67Tag::PermissionDenied => {}, + IOErrType67Tag::Unsupported => {}, } } -/// Increment Roc-owned payloads in RandomIOErr. -pub fn incref_random_ioerr(value: RandomIOErr, amount: isize) { +/// Increment Roc-owned payloads in IOErrType67. +pub fn incref_ioerr_type67(value: IOErrType67, amount: isize) { let _ = amount; match value.tag { - RandomIOErrTag::AlreadyExists => {}, - RandomIOErrTag::BrokenPipe => {}, - RandomIOErrTag::Interrupted => {}, - RandomIOErrTag::NotFound => {}, - RandomIOErrTag::Other => unsafe { + IOErrType67Tag::AlreadyExists => {}, + IOErrType67Tag::BrokenPipe => {}, + IOErrType67Tag::Interrupted => {}, + IOErrType67Tag::NotFound => {}, + IOErrType67Tag::Other => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); payload.incref(amount); }, - RandomIOErrTag::OutOfMemory => {}, - RandomIOErrTag::PermissionDenied => {}, - RandomIOErrTag::Unsupported => {}, + IOErrType67Tag::OutOfMemory => {}, + IOErrType67Tag::PermissionDenied => {}, + IOErrType67Tag::Unsupported => {}, + } +} + +/// Recursively decrement Roc-owned payloads in TryType70. +pub fn decref_try_type70(value: TryType70, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType70Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + decref_end_of_file_or_stdin_err(payload, roc_host); + }, + TryType70Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + { + let list = payload; + list.decref(roc_host); + } + }, + } +} + +/// Increment Roc-owned payloads in TryType70. +pub fn incref_try_type70(value: TryType70, amount: isize) { + let _ = amount; + match value.tag { + TryType70Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + incref_end_of_file_or_stdin_err(payload, amount); + }, + TryType70Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, + } +} + +/// Recursively decrement Roc-owned payloads in TryType73. +pub fn decref_try_type73(value: TryType73, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + TryType73Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + decref_ioerr_type67(payload, roc_host); + }, + TryType73Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + { + let list = payload; + list.decref(roc_host); + } + }, + } +} + +/// Increment Roc-owned payloads in TryType73. +pub fn incref_try_type73(value: TryType73, amount: isize) { + let _ = amount; + match value.tag { + TryType73Tag::Err => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); + incref_ioerr_type67(payload, amount); + }, + TryType73Tag::Ok => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.ok); + payload.incref(amount); + }, } } -/// Recursively decrement Roc-owned payloads in TryType43. -pub fn decref_try_type43(value: TryType43, roc_host: &RocHost) { +/// Recursively decrement Roc-owned payloads in TryType75. +pub fn decref_try_type75(value: TryType75, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType43Tag::Err => unsafe { + TryType75Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - decref_random_ioerr(payload, roc_host); + decref_ioerr_type77(payload, roc_host); }, - TryType43Tag::Ok => {}, + TryType75Tag::Ok => {}, } } -/// Increment Roc-owned payloads in TryType43. -pub fn incref_try_type43(value: TryType43, amount: isize) { +/// Increment Roc-owned payloads in TryType75. +pub fn incref_try_type75(value: TryType75, amount: isize) { let _ = amount; match value.tag { - TryType43Tag::Err => unsafe { + TryType75Tag::Err => unsafe { let payload = core::mem::ManuallyDrop::into_inner(value.payload.err); - incref_random_ioerr(payload, amount); + incref_ioerr_type77(payload, amount); + }, + TryType75Tag::Ok => {}, + } +} + +/// Recursively decrement Roc-owned payloads in IOErrType77. +pub fn decref_ioerr_type77(value: IOErrType77, roc_host: &RocHost) { + let _ = roc_host; + match value.tag { + IOErrType77Tag::AlreadyExists => {}, + IOErrType77Tag::BrokenPipe => {}, + IOErrType77Tag::Interrupted => {}, + IOErrType77Tag::NotFound => {}, + IOErrType77Tag::Other => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); + payload.decref(roc_host); }, - TryType43Tag::Ok => {}, + IOErrType77Tag::OutOfMemory => {}, + IOErrType77Tag::PermissionDenied => {}, + IOErrType77Tag::Unsupported => {}, } } -/// Recursively decrement Roc-owned payloads in TryType59. -pub fn decref_try_type59(value: TryType59, roc_host: &RocHost) { +/// Increment Roc-owned payloads in IOErrType77. +pub fn incref_ioerr_type77(value: IOErrType77, amount: isize) { + let _ = amount; + match value.tag { + IOErrType77Tag::AlreadyExists => {}, + IOErrType77Tag::BrokenPipe => {}, + IOErrType77Tag::Interrupted => {}, + IOErrType77Tag::NotFound => {}, + IOErrType77Tag::Other => unsafe { + let payload = core::mem::ManuallyDrop::into_inner(value.payload.other); + payload.incref(amount); + }, + IOErrType77Tag::OutOfMemory => {}, + IOErrType77Tag::PermissionDenied => {}, + IOErrType77Tag::Unsupported => {}, + } +} + +/// Recursively decrement Roc-owned payloads in TryType88. +pub fn decref_try_type88(value: TryType88, roc_host: &RocHost) { let _ = roc_host; match value.tag { - TryType59Tag::Err => {}, - TryType59Tag::Ok => {}, + TryType88Tag::Err => {}, + TryType88Tag::Ok => {}, } } -/// Increment Roc-owned payloads in TryType59. -pub fn incref_try_type59(value: TryType59, amount: isize) { +/// Increment Roc-owned payloads in TryType88. +pub fn incref_try_type88(value: TryType88, amount: isize) { let _ = amount; match value.tag { - TryType59Tag::Err => {}, - TryType59Tag::Ok => {}, + TryType88Tag::Err => {}, + TryType88Tag::Ok => {}, } } @@ -2023,117 +2825,125 @@ unsafe extern "C" { #[allow(improper_ctypes)] unsafe extern "C" { - /// Hosted symbol for Cmd.exec_exit_code! - /// Roc signature: Cmd => Try(I32, [CmdErr(Cmd.IOErr)]) - pub fn hosted_cmd_exec_exit_code(arg0: Cmd) -> TryType0; + /// Hosted symbol for Cmd.host_exec_exit_code! + /// Roc signature: Cmd => Try(I32, IOErr) + pub fn hosted_cmd_host_exec_exit_code(arg0: Cmd) -> TryType0; - /// Hosted symbol for Cmd.exec_output! - /// Roc signature: Cmd => Try({ stderr_utf8_lossy : Str, stdout_utf8 : Str }, [CmdErr(Cmd.IOErr), NonZeroExit({ exit_code : I32, stderr_utf8_lossy : Str, stdout_utf8_lossy : Str })]) - pub fn hosted_cmd_exec_output(arg0: Cmd) -> TryType8; + /// Hosted symbol for Cmd.host_exec_output! + /// Roc signature: Cmd => Try({ stderr_bytes : List(U8), stdout_bytes : List(U8) }, Try({ exit_code : I32, stderr_bytes : List(U8), stdout_bytes : List(U8) }, IOErr)) + pub fn hosted_cmd_host_exec_output(arg0: Cmd) -> TryType7; /// Hosted symbol for Dir.create! - /// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) - pub fn hosted_dir_create(arg0: RocStr) -> TryType12; + /// Roc signature: Str => Try({}, [DirErr(IOErr)]) + pub fn hosted_dir_create(arg0: RocStr) -> TryType13; /// Hosted symbol for Dir.create_all! - /// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) - pub fn hosted_dir_create_all(arg0: RocStr) -> TryType12; + /// Roc signature: Str => Try({}, [DirErr(IOErr)]) + pub fn hosted_dir_create_all(arg0: RocStr) -> TryType13; /// Hosted symbol for Dir.delete_all! - /// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) - pub fn hosted_dir_delete_all(arg0: RocStr) -> TryType12; + /// Roc signature: Str => Try({}, [DirErr(IOErr)]) + pub fn hosted_dir_delete_all(arg0: RocStr) -> TryType13; /// Hosted symbol for Dir.delete_empty! - /// Roc signature: Str => Try({}, [DirErr(Dir.IOErr)]) - pub fn hosted_dir_delete_empty(arg0: RocStr) -> TryType12; + /// Roc signature: Str => Try({}, [DirErr(IOErr)]) + pub fn hosted_dir_delete_empty(arg0: RocStr) -> TryType13; /// Hosted symbol for Dir.list! - /// Roc signature: Str => Try(List(Str), [DirErr(Dir.IOErr)]) - pub fn hosted_dir_list(arg0: RocStr) -> TryType17; + /// Roc signature: Str => Try(List(Str), [DirErr(IOErr)]) + pub fn hosted_dir_list(arg0: RocStr) -> TryType18; /// Hosted symbol for Env.cwd! - /// Roc signature: {} => Str - pub fn hosted_env_cwd() -> RocStr; + /// Roc signature: {} => Try(Str, [CwdUnavailable]) + pub fn hosted_env_cwd() -> TryType23; /// Hosted symbol for Env.exe_path! - /// Roc signature: {} => Str - pub fn hosted_env_exe_path() -> RocStr; + /// Roc signature: {} => Try(Str, [ExePathUnavailable]) + pub fn hosted_env_exe_path() -> TryType26; /// Hosted symbol for Env.var! - /// Roc signature: Str => Str - pub fn hosted_env_var(arg0: RocStr) -> RocStr; + /// Roc signature: Str => Try(Str, [VarNotFound(Str)]) + pub fn hosted_env_var(arg0: RocStr) -> TryType20; /// Hosted symbol for File.delete! - /// Roc signature: Str => Try({}, [FileErr(File.IOErr)]) - pub fn hosted_file_delete(arg0: RocStr) -> TryType27; + /// Roc signature: Str => Try({}, [FileErr(IOErr)]) + pub fn hosted_file_delete(arg0: RocStr) -> TryType34; /// Hosted symbol for File.read_bytes! - /// Roc signature: Str => Try(List(U8), [FileErr(File.IOErr)]) - pub fn hosted_file_read_bytes(arg0: RocStr) -> TryType21; + /// Roc signature: Str => Try(List(U8), [FileErr(IOErr)]) + pub fn hosted_file_read_bytes(arg0: RocStr) -> TryType28; /// Hosted symbol for File.read_utf8! - /// Roc signature: Str => Try(Str, [FileErr(File.IOErr)]) - pub fn hosted_file_read_utf8(arg0: RocStr) -> TryType29; + /// Roc signature: Str => Try(Str, [FileErr(IOErr)]) + pub fn hosted_file_read_utf8(arg0: RocStr) -> TryType36; /// Hosted symbol for File.write_bytes! - /// Roc signature: Str, List(U8) => Try({}, [FileErr(File.IOErr)]) - pub fn hosted_file_write_bytes(arg0: RocStr, arg1: RocListWith) -> TryType27; + /// Roc signature: Str, List(U8) => Try({}, [FileErr(IOErr)]) + pub fn hosted_file_write_bytes(arg0: RocStr, arg1: RocListWith) -> TryType34; /// Hosted symbol for File.write_utf8! - /// Roc signature: Str, Str => Try({}, [FileErr(File.IOErr)]) - pub fn hosted_file_write_utf8(arg0: RocStr, arg1: RocStr) -> TryType27; + /// Roc signature: Str, Str => Try({}, [FileErr(IOErr)]) + pub fn hosted_file_write_utf8(arg0: RocStr, arg1: RocStr) -> TryType34; /// Hosted symbol for Locale.all! /// Roc signature: {} => List(Str) pub fn hosted_locale_all() -> RocList; /// Hosted symbol for Locale.get! - /// Roc signature: {} => Str - pub fn hosted_locale_get() -> RocStr; - - /// Hosted symbol for Path.is_dir! - /// Roc signature: Str => Try(Bool, [PathErr(Path.IOErr)]) - pub fn hosted_path_is_dir(arg0: RocStr) -> TryType32; + /// Roc signature: {} => Try(Str, [NotAvailable]) + pub fn hosted_locale_get() -> TryType37; - /// Hosted symbol for Path.is_file! - /// Roc signature: Str => Try(Bool, [PathErr(Path.IOErr)]) - pub fn hosted_path_is_file(arg0: RocStr) -> TryType32; - - /// Hosted symbol for Path.is_sym_link! - /// Roc signature: Str => Try(Bool, [PathErr(Path.IOErr)]) - pub fn hosted_path_is_sym_link(arg0: RocStr) -> TryType32; + /// Hosted symbol for Path.host_path_type! + /// Roc signature: List(U8) => Try({ is_dir : Bool, is_file : Bool, is_sym_link : Bool }, IOErr) + pub fn hosted_path_type(arg0: RocListWith) -> TryType41; /// Hosted symbol for Random.seed_u32! - /// Roc signature: {} => Try(U32, [RandomErr(Random.IOErr)]) - pub fn hosted_random_seed_u32() -> TryType43; + /// Roc signature: {} => Try(U32, [RandomErr(IOErr)]) + pub fn hosted_random_seed_u32() -> TryType54; /// Hosted symbol for Random.seed_u64! - /// Roc signature: {} => Try(U64, [RandomErr(Random.IOErr)]) - pub fn hosted_random_seed_u64() -> TryType37; + /// Roc signature: {} => Try(U64, [RandomErr(IOErr)]) + pub fn hosted_random_seed_u64() -> TryType48; /// Hosted symbol for Sleep.millis! /// Roc signature: U64 => {} pub fn hosted_sleep_millis(arg0: u64); /// Hosted symbol for Stderr.line! - /// Roc signature: Str => {} - pub fn hosted_stderr_line(arg0: RocStr); + /// Roc signature: Str => Try({}, [StderrErr(IOErr)]) + pub fn hosted_stderr_line(arg0: RocStr) -> TryType58; /// Hosted symbol for Stderr.write! - /// Roc signature: Str => {} - pub fn hosted_stderr_write(arg0: RocStr); + /// Roc signature: Str => Try({}, [StderrErr(IOErr)]) + pub fn hosted_stderr_write(arg0: RocStr) -> TryType58; + + /// Hosted symbol for Stderr.write_bytes! + /// Roc signature: List(U8) => Try({}, [StderrErr(IOErr)]) + pub fn hosted_stderr_write_bytes(arg0: RocListWith) -> TryType58; + + /// Hosted symbol for Stdin.bytes! + /// Roc signature: {} => Try(List(U8), [EndOfFile, StdinErr(IOErr)]) + pub fn hosted_stdin_bytes() -> TryType70; /// Hosted symbol for Stdin.line! - /// Roc signature: {} => Str - pub fn hosted_stdin_line() -> RocStr; + /// Roc signature: {} => Try(Str, [EndOfFile, StdinErr(IOErr)]) + pub fn hosted_stdin_line() -> TryType65; + + /// Hosted symbol for Stdin.read_to_end! + /// Roc signature: {} => Try(List(U8), [StdinErr(IOErr)]) + pub fn hosted_stdin_read_to_end() -> TryType73; /// Hosted symbol for Stdout.line! - /// Roc signature: Str => {} - pub fn hosted_stdout_line(arg0: RocStr); + /// Roc signature: Str => Try({}, [StdoutErr(IOErr)]) + pub fn hosted_stdout_line(arg0: RocStr) -> TryType75; /// Hosted symbol for Stdout.write! - /// Roc signature: Str => {} - pub fn hosted_stdout_write(arg0: RocStr); + /// Roc signature: Str => Try({}, [StdoutErr(IOErr)]) + pub fn hosted_stdout_write(arg0: RocStr) -> TryType75; + + /// Hosted symbol for Stdout.write_bytes! + /// Roc signature: List(U8) => Try({}, [StdoutErr(IOErr)]) + pub fn hosted_stdout_write_bytes(arg0: RocListWith) -> TryType75; /// Hosted symbol for Tty.disable_raw_mode! /// Roc signature: {} => {} diff --git a/tests/cmd-test.roc b/tests/cmd-test.roc new file mode 100644 index 00000000..773fdf47 --- /dev/null +++ b/tests/cmd-test.roc @@ -0,0 +1,101 @@ +app [main!] { pf: platform "../platform/main.roc" } + +import pf.Stdout +import pf.Cmd + +# Tests all error cases in Cmd functions. + +main! = |_args| { + + # exec! + expect_err( + Cmd.exec!("blablaXYZ", []), + "Err(FailedToGetExitCode({ command: \"{ cmd: blablaXYZ, args: }\", err: NotFound }))" + )? + + expect_err( + Cmd.exec!("cat", ["non_existent.txt"]), + "Err(ExecFailed({ command: \"cat non_existent.txt\", exit_code: 1 }))" + )? + + # exec_cmd! + expect_err( + Cmd.new("blablaXYZ").exec_cmd!(), + "Err(FailedToGetExitCode({ command: \"{ cmd: blablaXYZ, args: }\", err: NotFound }))" + )? + + expect_err( + Cmd.new("cat").arg("non_existent.txt").exec_cmd!(), + "Err(ExecCmdFailed({ command: \"{ cmd: cat, args: non_existent.txt }\", exit_code: 1 }))" + )? + + # exec_output! + expect_err( + Cmd.new("blablaXYZ").exec_output!(), + "Err(FailedToGetExitCode({ command: \"{ cmd: blablaXYZ, args: }\", err: NotFound }))" + )? + + expect_err( + Cmd.new("cat").arg("non_existent.txt").exec_output!(), + "Err(NonZeroExitCode({ command: \"{ cmd: cat, args: non_existent.txt }\", exit_code: 1, stderr_utf8_lossy: \"cat: non_existent.txt: No such file or directory\n\", stdout_utf8_lossy: \"\" }))" + )? + + # Test StdoutContainsInvalidUtf8 - blocked by compiler bug + expect_err( + Cmd.new("printf").args(["\\377\\376"]).exec_output!(), + "Err(StdoutContainsInvalidUtf8({ err: BadUtf8({ index: 0, problem: InvalidStartByte }), cmd_str: \"{ cmd: printf, args: \\\\377\\\\376 }\" }))" + )? + + # exec_output_bytes! + expect_err( + Cmd.new("blablaXYZ").exec_output_bytes!(), + "Err(FailedToGetExitCodeB(NotFound))" + )? + + expect_err( + Cmd.new("cat").arg("non_existent.txt").exec_output_bytes!(), + "Err(NonZeroExitCodeB({ exit_code: 1, stderr_bytes: [99, 97, 116, 58, 32, 110, 111, 110, 95, 101, 120, 105, 115, 116, 101, 110, 116, 46, 116, 120, 116, 58, 32, 78, 111, 32, 115, 117, 99, 104, 32, 102, 105, 108, 101, 32, 111, 114, 32, 100, 105, 114, 101, 99, 116, 111, 114, 121, 10], stdout_bytes: [] }))" + )? + + # exec_exit_code! + expect_err( + Cmd.new("blablaXYZ").exec_exit_code!(), + "Err(FailedToGetExitCode({ command: \"{ cmd: blablaXYZ, args: }\", err: NotFound }))" + )? + + # exec_exit_code! with non-zero exit code is not an error - it returns the exit code + exit_code = + Cmd.new("cat") + .arg("non_existent.txt") + .exec_exit_code!()? + + if exit_code == 1 { + Ok({})? + } else { + Err(FailedExpectation( + \\- Expected: + \\1 + \\ + \\- Got: + \\${Str.inspect(exit_code)} + ))? + } + + _ = Stdout.line!("All tests passed.") + + Ok({}) +} + +expect_err = |err, expected_str| { + if Str.inspect(err) == expected_str { + Ok({}) + } else { + Err(FailedExpectation( + \\- Expected: + \\${expected_str} + + \\- Got: + \\${Str.inspect(err)} + )) + } +} diff --git a/tests/env.todoroc b/tests/env.todoroc new file mode 100644 index 00000000..2b5e37b2 --- /dev/null +++ b/tests/env.todoroc @@ -0,0 +1,28 @@ +app [main!] { pf: platform "../platform/main.roc" } + +import pf.Stdout +import pf.Env + +main! = |_args| { + Stdout.line!( + \\Testing Env module functions... + \\ + \\Testing Env.cwd!: + )? + #cwd = Env.cwd!({})? + # Stdout.line!( + # \\cwd: ${Path.display(cwd)} + # \\ + # \\Testing Env.exe_path!: + # )? + # exe_path = Env.exe_path!({})? + # Stdout.line!( + # \\exe_path: ${Path.display(exe_path)} + # )? + + Stdout.line!( + \\All tests executed TODO. + )? + + Ok({}) +} \ No newline at end of file