From dabd3ce1efe52b270f0b72dc360de0d6bcec250d Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Sun, 5 Jan 2025 06:15:34 -0800 Subject: [PATCH] Convert to snake_case --- README.md | 8 +- build.roc | 92 ++++----- examples/command.roc | 12 +- examples/dir.roc | 28 +-- examples/echo.roc | 14 +- examples/env.roc | 22 +- examples/error-handling.roc | 36 ++-- examples/file-upload-form.roc | 28 +-- examples/file.roc | 16 +- examples/hello-web.roc | 12 +- examples/init-basic.roc | 8 +- examples/result.roc | 16 +- examples/sqlite.roc | 37 ++-- examples/temp-dir.roc | 6 +- examples/todos.roc | 170 ++++++++------- platform/Cmd.roc | 46 ++--- platform/Dir.roc | 10 +- platform/Env.roc | 46 ++--- platform/EnvDecoding.roc | 119 +++++------ platform/File.roc | 44 ++-- platform/FileMetadata.roc | 8 +- platform/Http.roc | 12 +- platform/InternalCmd.roc | 2 +- platform/InternalDateTime.roc | 95 +++++---- platform/InternalHttp.roc | 26 +-- platform/InternalIOErr.roc | 2 +- platform/InternalPath.roc | 14 +- platform/MultipartFormData.roc | 363 +++++++++++++++++---------------- platform/Path.roc | 126 ++++++------ platform/SplitList.roc | 74 +++---- platform/Sqlite.roc | 284 +++++++++++++------------- platform/Stderr.roc | 24 +-- platform/Stdout.roc | 24 +-- platform/Tcp.roc | 48 ++--- platform/Url.roc | 252 +++++++++++------------ platform/Utc.roc | 40 ++-- platform/libapp.roc | 9 +- platform/main.roc | 42 ++-- 38 files changed, 1112 insertions(+), 1103 deletions(-) diff --git a/README.md b/README.md index 878220d..6b31640 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,15 @@ init! = \{} -> Ok {} respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \req, _ -> # Log request datetime, method and url - datetime = Utc.to_iso_8601 (Utc.now! {}) + datetime = Utc.to_iso_8601(Utc.now!({})) - try Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)" + Stdout.line!("$(datetime) $(Inspect.toStr req.method) $(req.uri)")? - Ok { + Ok({ status: 200, headers: [], body: Str.toUtf8 "Hello from server
", - } + }) ``` diff --git a/build.roc b/build.roc index 5bf3261..ac0ad34 100644 --- a/build.roc +++ b/build.roc @@ -17,101 +17,101 @@ main : Task {} _ main = cli_parser = - Arg.Opt.maybeStr { short: "p", long: "roc", help: "Path to the roc executable. Can be just `roc` or a full path." } - |> Arg.Cli.finish { + Arg.Opt.maybe_str({ short: "p", long: "roc", help: "Path to the roc executable. Can be just `roc` or a full path." }) + |> Arg.Cli.finish({ name: "basic-webserver-builder", version: "", authors: ["Luke Boswell "], description: "Generates all files needed by Roc to use this basic-cli platform.", - } - |> Arg.Cli.assertValid + }) + |> Arg.Cli.assert_valid - when Arg.Cli.parseOrDisplayMessage cli_parser (Arg.list! {}) is - Ok parsed_args -> run parsed_args - Err err_msg -> Task.err (Exit 1 err_msg) + when Arg.Cli.parse_or_display_message(cli_parser, Arg.list!({})) is + Ok(parsed_args) -> run(parsed_args) + Err(err_msg) -> Task.err(Exit(1, err_msg)) run : Result Str err -> Task {} _ run = \maybe_roc -> # roc_cmd may be a path or just roc - roc_cmd = maybe_roc |> Result.withDefault "roc" + roc_cmd = maybe_roc |> Result.with_default("roc") - roc_version! roc_cmd + roc_version!(roc_cmd) os_and_arch = get_os_and_arch! - stub_lib_path = "platform/libapp.$(stub_file_extension os_and_arch)" + stub_lib_path = "platform/libapp.$(stub_file_extension(os_and_arch))" - build_stub_app_lib! roc_cmd stub_lib_path + build_stub_app_lib!(roc_cmd, stub_lib_path) cargo_build_host! rust_target_folder = get_rust_target_folder! - copy_host_lib! os_and_arch rust_target_folder + copy_host_lib!(os_and_arch, rust_target_folder) - preprocess_host! roc_cmd stub_lib_path rust_target_folder + preprocess_host!(roc_cmd, stub_lib_path, rust_target_folder) - info! "Successfully built platform files!" + info!("Successfully built platform files!") roc_version : Str -> Task {} _ roc_version = \roc_cmd -> - info! "Checking provided roc; executing `$(roc_cmd) version`:" + info!("Checking provided roc; executing `$(roc_cmd) version`:") roc_cmd - |> Cmd.exec ["version"] - |> Task.mapErr! RocVersionCheckFailed + |> Cmd.exec(["version"]) + |> Task.map_err!(RocVersionCheckFailed) get_os_and_arch : Task OSAndArch _ get_os_and_arch = - info! "Getting the native operating system and architecture..." + info!("Getting the native operating system and architecture...") Env.platform - |> Task.await convert_os_and_arch + |> Task.await(convert_os_and_arch) build_stub_app_lib : Str, Str -> Task {} _ build_stub_app_lib = \roc_cmd, stub_lib_path -> - info! "Building stubbed app shared library ..." + info!("Building stubbed app shared library ...") roc_cmd - |> Cmd.exec ["build", "--lib", "platform/libapp.roc", "--output", stub_lib_path, "--optimize"] - |> Task.mapErr! ErrBuildingAppStub + |> Cmd.exec(["build", "--lib", "platform/libapp.roc", "--output", stub_lib_path, "--optimize"]) + |> Task.map_err!(ErrBuildingAppStub) get_rust_target_folder : Task Str _ get_rust_target_folder = - when Env.var "CARGO_BUILD_TARGET" |> Task.result! is - Ok target_env_var -> - if Str.isEmpty target_env_var then - Task.ok "target/release/" + when Env.var("CARGO_BUILD_TARGET") |> Task.result! is + Ok(target_env_var) -> + if Str.is_empty(target_env_var) then + Task.ok("target/release/") else - Task.ok "target/$(target_env_var)/release/" + Task.ok("target/$(target_env_var)/release/") - Err e -> - info! "Failed to get env var CARGO_BUILD_TARGET with error $(Inspect.toStr e). Assuming default CARGO_BUILD_TARGET (native)..." + Err(e) -> + info!("Failed to get env var CARGO_BUILD_TARGET with error $(Inspect.to_str(e)). Assuming default CARGO_BUILD_TARGET (native)...") - Task.ok "target/release/" + Task.ok("target/release/") cargo_build_host : Task {} _ cargo_build_host = - info! "Building rust host ..." + info!("Building rust host ...") "cargo" - |> Cmd.exec ["build", "--release"] - |> Task.mapErr! ErrBuildingHostBinaries + |> Cmd.exec(["build", "--release"]) + |> Task.map_err!(ErrBuildingHostBinaries) copy_host_lib : OSAndArch, Str -> Task {} _ copy_host_lib = \os_and_arch, rust_target_folder -> host_build_path = "$(rust_target_folder)libhost.a" - host_dest_path = "platform/$(prebuilt_static_lib_file os_and_arch)" + host_dest_path = "platform/$(prebuilt_static_lib_file(os_and_arch))" - info! "Moving the prebuilt binary from $(host_build_path) to $(host_dest_path) ..." + info!("Moving the prebuilt binary from $(host_build_path) to $(host_dest_path) ...") "cp" - |> Cmd.exec [host_build_path, host_dest_path] - |> Task.mapErr! ErrMovingPrebuiltLegacyBinary + |> Cmd.exec([host_build_path, host_dest_path]) + |> Task.map_err!(ErrMovingPrebuiltLegacyBinary) OSAndArch : [ MacosArm64, @@ -125,11 +125,11 @@ OSAndArch : [ convert_os_and_arch : _ -> Task OSAndArch _ convert_os_and_arch = \{ os, arch } -> when (os, arch) is - (MACOS, AARCH64) -> Task.ok MacosArm64 - (MACOS, X64) -> Task.ok MacosX64 - (LINUX, AARCH64) -> Task.ok LinuxArm64 - (LINUX, X64) -> Task.ok LinuxX64 - _ -> Task.err (UnsupportedNative os arch) + (MACOS, AARCH64) -> Task.ok(MacosArm64) + (MACOS, X64) -> Task.ok(MacosX64) + (LINUX, AARCH64) -> Task.ok(LinuxArm64) + (LINUX, X64) -> Task.ok(LinuxX64) + _ -> Task.err(UnsupportedNative(os, arch)) stub_file_extension : OSAndArch -> Str stub_file_extension = \os_and_arch -> @@ -150,13 +150,13 @@ prebuilt_static_lib_file = \os_and_arch -> preprocess_host : Str, Str, Str -> Task {} _ preprocess_host = \roc_cmd, stub_lib_path, rust_target_folder -> - info! "Preprocessing surgical host ..." + info!("Preprocessing surgical host ...") surgical_build_path = "$(rust_target_folder)host" roc_cmd - |> Cmd.exec ["preprocess-host", surgical_build_path, "platform/main.roc", stub_lib_path] - |> Task.mapErr! ErrPreprocessingSurgicalBinary + |> Cmd.exec(["preprocess-host", surgical_build_path, "platform/main.roc", stub_lib_path]) + |> Task.map_err!(ErrPreprocessingSurgicalBinary) info : Str -> Task {} _ info = \msg -> - Stdout.line! "\u(001b)[34mINFO:\u(001b)[0m $(msg)" + Stdout.line!("\u(001b)[34mINFO:\u(001b)[0m $(msg)") diff --git a/examples/command.roc b/examples/command.roc index 62d81be..ec026e1 100644 --- a/examples/command.roc +++ b/examples/command.roc @@ -7,18 +7,18 @@ import pf.Utc Model : {} init! : {} => Result Model _ -init! = \{} -> Ok {} +init! = \{} -> Ok({}) respond! : Request, Model => Result Response [CmdStatusErr _] respond! = \req, _ -> # Log request date, method and url using echo program - datetime = Utc.to_iso_8601 (Utc.now! {}) + datetime = Utc.to_iso_8601(Utc.now!({})) - try Cmd.exec! "echo" ["$(datetime) $(Inspect.toStr req.method) $(req.uri)"] + Cmd.exec!("echo", ["$(datetime) $(Inspect.to_str(req.method)) $(req.uri)"])? - Ok { + Ok({ status: 200, headers: [], - body: Str.toUtf8 "Command succeeded.", - } + body: Str.to_utf8("Command succeeded."), + }) diff --git a/examples/dir.roc b/examples/dir.roc index 108749e..fce92da 100644 --- a/examples/dir.roc +++ b/examples/dir.roc @@ -12,35 +12,35 @@ init! : {} => Result Model _ init! = \{} -> # Get current working directory cwd = - Env.cwd! {} - |> Result.mapErr? \CwdUnavailable -> Exit 1 "Unable to read current working directory" + Env.cwd!({}) + |> Result.map_err?(\CwdUnavailable -> Exit(1, "Unable to read current working directory")) - try Stdout.line! "The current working directory is $(Path.display cwd)" + Stdout.line!("The current working directory is $(Path.display(cwd))")? # Try to set cwd to examples - Env.set_cwd! (Path.from_str "examples/") - |> Result.mapErr? \InvalidCwd -> Exit 1 "Unable to set cwd to examples/" + Env.set_cwd!(Path.from_str("examples/")) + |> Result.map_err?(\InvalidCwd -> Exit(1, "Unable to set cwd to examples/")) - try Stdout.line! "Set cwd to examples/" + Stdout.line!("Set cwd to examples/")? # List contents of examples directory paths = - Dir.list! "./" - |> Result.mapErr? \DirErr err -> Exit 1 "Error reading directory ./:\n\t$(Inspect.toStr err)" + Dir.list!("./") + |> Result.map_err?(\DirErr(err) -> Exit(1, "Error reading directory ./:\n\t$(Inspect.to_str(err))")) paths - |> List.map Path.display - |> Str.joinWith "," + |> List.map(Path.display) + |> Str.join_with(",") |> \paths_str -> "The paths are;\n$(paths_str)" |> Stdout.line! |> try - Ok {} + Ok({}) respond! : Request, Model => Result Response [] respond! = \_, _ -> - Ok { + Ok({ status: 200, headers: [], - body: Str.toUtf8 "Logged request", - } + body: Str.to_utf8("Logged request"), + }) diff --git a/examples/echo.roc b/examples/echo.roc index 11c3371..b1c25f9 100644 --- a/examples/echo.roc +++ b/examples/echo.roc @@ -7,19 +7,19 @@ import pf.Utc Model : {} init! : {} => Result Model [] -init! = \{} -> Ok {} +init! = \{} -> Ok({}) respond! : Request, Model => Result Response [StdoutErr _] respond! = \req, _ -> # Log request datetime, method and url - datetime = Utc.to_iso_8601 (Utc.now! {}) + datetime = Utc.to_iso_8601(Utc.now!({})) - try Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)" + Stdout.line!("$(datetime) $(Inspect.to_str(req.method)) $(req.uri)")? # Respond with request body - if List.isEmpty req.body then - success [] + if List.is_empty(req.body) then + success([]) else - success req.body + success(req.body) -success = \body -> Ok { status: 200, headers: [], body } +success = \body -> Ok({ status: 200, headers: [], body }) diff --git a/examples/env.roc b/examples/env.roc index c24671d..5de373e 100644 --- a/examples/env.roc +++ b/examples/env.roc @@ -10,9 +10,9 @@ init! : {} => Result Model [Exit I32 Str]_ init! = \{} -> # Check if DEBUG environment variable is set - when Env.var! "DEBUG" is - Ok var if !(Str.isEmpty var) -> Ok DebugPrintMode - _ -> Ok NonDebugMode + when Env.var!("DEBUG") is + Ok(var) if !(Str.is_empty(var)) -> Ok(DebugPrintMode) + _ -> Ok(NonDebugMode) respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \_, debug -> @@ -20,19 +20,19 @@ respond! = \_, debug -> DebugPrintMode -> # Respond with all the current environment variables vars : Dict Str Str - vars = Env.dict! {} + vars = Env.dict!({}) # Convert the Dict to a list of key-value pairs body = vars - |> Dict.toList - |> List.map \(k, v) -> "$(k): $(v)" - |> Str.joinWith "\n" - |> Str.concat "\n" - |> Str.toUtf8 + |> Dict.to_list + |> List.map(\(k, v) -> "$(k): $(v)") + |> Str.join_with("\n") + |> Str.concat("\n") + |> Str.to_utf8 - Ok { status: 200, headers: [], body } + Ok({ status: 200, headers: [], body }) NonDebugMode -> # Respond with a message that DEBUG is not set - Ok { status: 200, headers: [], body: Str.toUtf8 "DEBUG var not set" } + Ok({ status: 200, headers: [], body: Str.to_utf8("DEBUG var not set") }) diff --git a/examples/error-handling.roc b/examples/error-handling.roc index 636b4c6..78141d2 100644 --- a/examples/error-handling.roc +++ b/examples/error-handling.roc @@ -9,10 +9,10 @@ import pf.Env Model : {} init! : {} => Result Model [] -init! = \{} -> Ok {} +init! = \{} -> Ok({}) respond! : Request, Model => Result Response [ServerErr Str]_ -respond! = \req, _ -> handle_req! req |> Result.mapErr map_app_err +respond! = \req, _ -> handle_req!(req) |> Result.map_err(map_app_err) AppError : [ EnvVarNotSet Str, @@ -23,46 +23,46 @@ AppError : [ map_app_err : AppError -> [ServerErr Str] map_app_err = \app_err -> when app_err is - EnvVarNotSet env_var_name -> ServerErr "Environment variable \"$(env_var_name)\" was not set." - BadBody err -> ServerErr "Http error fetching content:\n\t$(Inspect.toStr err)" - StdoutErr err -> ServerErr "Stdout error logging request:\n\t$(err)" + EnvVarNotSet(env_var_name) -> ServerErr("Environment variable \"$(env_var_name)\" was not set.") + BadBody(err) -> ServerErr("Http error fetching content:\n\t$(Inspect.to_str(err))") + StdoutErr(err) -> ServerErr("Stdout error logging request:\n\t$(err)") # Here we use AppError to ensure all errors must be handled within our application. handle_req! : Request => Result Response AppError handle_req! = \req -> # Log the date, time, method, and url to stdout - try log_request! req + log_request!(req)? # Read environment variable - url = try read_env_var! "TARGET_URL" + url = read_env_var!("TARGET_URL")? # Fetch content of url - content = try fetch_content! url + content = fetch_content!(url)? # Respond with the website content - response_with_code! 200 content + response_with_code!(200, content) log_request! : Request => Result {} [StdoutErr Str] log_request! = \req -> - datetime = Utc.to_iso_8601 (Utc.now! {}) + datetime = Utc.to_iso_8601(Utc.now!({})) - Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)" - |> Result.mapErr \err -> StdoutErr (Inspect.toStr err) + Stdout.line!("$(datetime) $(Inspect.to_str(req.method)) $(req.uri)") + |> Result.map_err(\err -> StdoutErr(Inspect.to_str(err))) read_env_var! : Str => Result Str [EnvVarNotSet Str] read_env_var! = \env_var_name -> - Env.var! env_var_name - |> Result.mapErr \_ -> EnvVarNotSet env_var_name + Env.var!(env_var_name) + |> Result.map_err(\_ -> EnvVarNotSet(env_var_name)) fetch_content! : Str => Result Str _ fetch_content! = \url -> - Http.get_utf8! url + Http.get_utf8!(url) # Respond with the given status code and body response_with_code! : U16, Str => Result Response * response_with_code! = \code, body -> - Ok { + Ok({ status: code, headers: [], - body: Str.toUtf8 body, - } + body: Str.to_utf8(body), + }) diff --git a/examples/file-upload-form.roc b/examples/file-upload-form.roc index 8164126..ee7b0ae 100644 --- a/examples/file-upload-form.roc +++ b/examples/file-upload-form.roc @@ -11,7 +11,7 @@ import pf.MultipartFormData Model : {} init! : {} => Result Model [] -init! = \{} -> Ok {} +init! = \{} -> Ok({}) respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \req, _ -> @@ -36,15 +36,15 @@ respond! = \req, _ -> """ - |> Str.toUtf8 + |> Str.to_utf8 - Ok { + Ok({ status: 200, headers: [ { name: "Content-Type", value: "text/html" }, ], body, - } + }) else if req.method == POST then page = \src -> """ @@ -70,25 +70,25 @@ respond! = \req, _ -> """ - |> Str.toUtf8 + |> Str.to_utf8 maybe_image = { headers: req.headers, body: req.body } |> MultipartFormData.parse_multipart_form_data - |> Result.try List.first - |> Result.map .data - |> Result.map Base64.encode + |> Result.try(List.first) + |> Result.map(.data) + |> Result.map(Base64.encode) when maybe_image is - Ok img -> - Ok { + Ok(img) -> + Ok({ status: 200, headers: [ { name: "Content-Type", value: "text/html" }, ], - body: page img, - } + body: page(img), + }) - Err err -> Ok { status: 500, headers: [], body: err |> Inspect.toStr |> Str.toUtf8 } + Err(err) -> Ok({ status: 500, headers: [], body: err |> Inspect.to_str |> Str.to_utf8 }) else - Ok { status: 500, headers: [], body: [] } + Ok({ status: 500, headers: [], body: [] }) diff --git a/examples/file.roc b/examples/file.roc index ec162e3..68248ac 100644 --- a/examples/file.roc +++ b/examples/file.roc @@ -11,19 +11,19 @@ Model : Str init! : {} => Result Model [Exit I32 Str]_ init! = \{} -> # Read the contents of examples/file.roc - when File.read_utf8! "examples/file.roc" is - Ok contents -> - Ok "Source code of current program:\n\n$(contents)" + when File.read_utf8!("examples/file.roc") is + Ok(contents) -> + Ok("Source code of current program:\n\n$(contents)") - Err (FileReadErr path err) -> - Exit -1 "Failed to launch server!\nError reading file $(Path.display path):\n\t$(Inspect.toStr err)" + Err(FileReadErr(path, err)) -> + Exit(-1, "Failed to launch server!\nError reading file $(Path.display(path)):\n\t$(Inspect.to_str(err))") |> Err - Err (FileReadUtf8Err path _) -> - Exit -2 "Failed to launch server!\nError: failed to read file $(Path.display path) as utf8." + Err(FileReadUtf8Err(path, _)) -> + Exit(-2, "Failed to launch server!\nError: failed to read file $(Path.display(path)) as utf8.") |> Err respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \_, model -> # If the server launched, the model contains the file content. - Ok { status: 200, headers: [], body: Str.toUtf8 model } + Ok({ status: 200, headers: [], body: Str.to_utf8(model) }) diff --git a/examples/hello-web.roc b/examples/hello-web.roc index 3b5926a..762204d 100644 --- a/examples/hello-web.roc +++ b/examples/hello-web.roc @@ -11,17 +11,17 @@ Model : {} # generate css by running `tailwindcss`,... # In this case we don't have anything to initialize, so it is just `Ok {}`. init! : {} => Result Model [] -init! = \{} -> Ok {} +init! = \{} -> Ok({}) respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \req, _ -> # Log request datetime, method and url - datetime = Utc.to_iso_8601 (Utc.now! {}) + datetime = Utc.to_iso_8601(Utc.now!({})) - try Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)" + Stdout.line!("$(datetime) $(Inspect.to_str(req.method)) $(req.uri)")? - Ok { + Ok({ status: 200, headers: [], - body: Str.toUtf8 "Hello from server
", - } + body: Str.to_utf8("Hello from server
"), + }) diff --git a/examples/init-basic.roc b/examples/init-basic.roc index 38028ed..9955855 100644 --- a/examples/init-basic.roc +++ b/examples/init-basic.roc @@ -11,13 +11,13 @@ Model : Str # generate css by running `tailwindcss`,... # In this example it is just `Ok "🎁"`. init! : {} => Result Model [] -init! = \{} -> Ok "🎁" +init! = \{} -> Ok("🎁") respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \req, model -> # Log request datetime, method and url - datetime = Utc.to_iso_8601 (Utc.now! {}) + datetime = Utc.to_iso_8601(Utc.now!({})) - try Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)" + Stdout.line!("$(datetime) $(Inspect.to_str(req.method)) $(req.uri)")? - Ok { status: 200, headers: [], body: Str.toUtf8 "init gave me $(model)" } + Ok({ status: 200, headers: [], body: Str.to_utf8("init gave me $(model)") }) diff --git a/examples/result.roc b/examples/result.roc index f1e63ab..5290ae0 100644 --- a/examples/result.roc +++ b/examples/result.roc @@ -5,14 +5,14 @@ import pf.Http exposing [Request, Response] Model : {} init! : {} => Result Model [] -init! = \{} -> Ok {} +init! = \{} -> Ok({}) respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \_, _ -> - when check_file! "good" is - Ok Good -> Ok { status: 200, headers: [], body: Str.toUtf8 "GOOD" } - Ok Bad -> Ok { status: 200, headers: [], body: Str.toUtf8 "BAD" } - Err IOError -> Ok { status: 500, headers: [], body: Str.toUtf8 "ERROR: IoError when executing checkFile!." } + when check_file!("good") is + Ok(Good) -> Ok({ status: 200, headers: [], body: Str.to_utf8("GOOD") }) + Ok(Bad) -> Ok({ status: 200, headers: [], body: Str.to_utf8("BAD") }) + Err(IOError) -> Ok({ status: 500, headers: [], body: Str.to_utf8("ERROR: IoError when executing checkFile!.") }) # imagine this function does some IO operation # and returns a Result, succeding with a tag either Good or Bad, @@ -20,8 +20,8 @@ respond! = \_, _ -> check_file! : Str => Result [Good, Bad] [IOError] check_file! = \str -> if str == "good" then - Ok Good + Ok(Good) else if str == "bad" then - Ok Bad + Ok(Bad) else - Err IOError + Err(IOError) diff --git a/examples/sqlite.roc b/examples/sqlite.roc index effed49..af355d5 100644 --- a/examples/sqlite.roc +++ b/examples/sqlite.roc @@ -11,42 +11,39 @@ init! : {} => Result Model _ init! = \{} -> # Read DB_PATH environment variable db_path = - Env.var! "DB_PATH" - |> Result.mapErr \_ -> ServerErr "DB_PATH not set on environment" - |> try + Env.var!("DB_PATH") + |> Result.map_err(\_ -> ServerErr("DB_PATH not set on environment"))? stmt = - Sqlite.prepare! { + Sqlite.prepare!({ path: db_path, query: "SELECT id, task FROM todos WHERE status = :status;", - } - |> Result.mapErr \err -> ServerErr "Failed to prepare Sqlite statement: $(Inspect.toStr err)" - |> try + }) + |> Result.map_err(\err -> ServerErr("Failed to prepare Sqlite statement: $(Inspect.to_str(err))"))? - Ok { stmt } + Ok({ stmt }) respond! : Request, Model => Result Response _ respond! = \_, { stmt } -> # Query todos table strings : Str strings = - Sqlite.query_many_prepared! { + Sqlite.query_many_prepared!({ stmt, - bindings: [{ name: ":status", value: String "completed" }], + bindings: [{ name: ":status", value: String("completed") }], rows: { Sqlite.decode_record <- - id: Sqlite.i64 "id", - task: Sqlite.str "task", + id: Sqlite.i64("id"), + task: Sqlite.str("task"), }, - } - |> try - |> List.map \{ id, task } -> "row $(Num.toStr id), task: $(task)" - |> Str.joinWith "\n" + })? + |> List.map(\{ id, task } -> "row $(Num.to_str(id)), task: $(task)") + |> Str.join_with("\n") # Print out the results - try Stdout.line! strings + Stdout.line!(strings)? - Ok { + Ok({ status: 200, headers: [{ name: "Content-Type", value: "text/html; charset=utf-8" }], - body: Str.toUtf8 strings, - } + body: Str.to_utf8(strings), + }) diff --git a/examples/temp-dir.roc b/examples/temp-dir.roc index b9d002d..8e6fea5 100644 --- a/examples/temp-dir.roc +++ b/examples/temp-dir.roc @@ -12,11 +12,11 @@ import pf.Env Model : {} init! : {} => Result Model [] -init! = \{} -> Ok {} +init! = \{} -> Ok({}) respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \_, _ -> - temp_dir_str = Path.display (Env.temp_dir! {}) + temp_dir_str = Path.display(Env.temp_dir!({})) - Ok { status: 200, headers: [], body: Str.toUtf8 "The temp dir path is $(temp_dir_str)" } + Ok({ status: 200, headers: [], body: Str.to_utf8("The temp dir path is $(temp_dir_str)") }) diff --git a/examples/todos.roc b/examples/todos.roc index 419221d..441cf45 100644 --- a/examples/todos.roc +++ b/examples/todos.roc @@ -20,36 +20,36 @@ Model : { init! : {} => Result Model [Exit I32 Str]_ init! = \{} -> - db_path = try read_env_var! "DB_PATH" + db_path = read_env_var!("DB_PATH")? - list_todos_stmt = try prepare_stmt! db_path "SELECT id, task, status FROM todos" - create_todo_stmt = try prepare_stmt! db_path "INSERT INTO todos (task, status) VALUES (:task, :status)" - last_created_todo_stmt = try prepare_stmt! db_path "SELECT id, task, status FROM todos WHERE id = last_insert_rowid()" - begin_stmt = try prepare_stmt! db_path "BEGIN" - end_stmt = try prepare_stmt! db_path "END" - rollback_stmt = try prepare_stmt! db_path "ROLLBACK" + list_todos_stmt = prepare_stmt!(db_path, "SELECT id, task, status FROM todos")? + create_todo_stmt = prepare_stmt!(db_path, "INSERT INTO todos (task, status) VALUES (:task, :status)")? + last_created_todo_stmt = prepare_stmt!(db_path, "SELECT id, task, status FROM todos WHERE id = last_insert_rowid()")? + begin_stmt = prepare_stmt!(db_path, "BEGIN")? + end_stmt = prepare_stmt!(db_path, "END")? + rollback_stmt = prepare_stmt!(db_path, "ROLLBACK")? - Ok { list_todos_stmt, create_todo_stmt, last_created_todo_stmt, begin_stmt, end_stmt, rollback_stmt } + Ok({ list_todos_stmt, create_todo_stmt, last_created_todo_stmt, begin_stmt, end_stmt, rollback_stmt }) respond! : Request, Model => Result Response [ServerErr Str]_ respond! = \req, model -> response_task = - try log_request! req + log_request!(req)? split_url = req.uri |> Url.from_str |> Url.path - |> Str.splitOn "/" + |> Str.split_on("/") # Route to handler based on url path when split_url is - ["", ""] -> byte_response 200 todoHtml - ["", "todos", ..] -> route_todos! model req - _ -> text_response 404 "URL Not Found (404)" + ["", ""] -> byte_response(200, todo_html) + ["", "todos", ..] -> route_todos!(model, req) + _ -> text_response(404, "URL Not Found (404)") # Handle any application errors - response_task |> Result.mapErr map_app_err + response_task |> Result.map_err(map_app_err) AppError : [ EnvVarNotSet Str, @@ -59,181 +59,179 @@ AppError : [ map_app_err : AppError -> [ServerErr Str] map_app_err = \app_err -> when app_err is - EnvVarNotSet var_name -> ServerErr "Environment variable \"$(var_name)\" was not set. Please set it to the path of todos.db" - StdoutErr msg -> ServerErr msg + EnvVarNotSet(var_name) -> ServerErr("Environment variable \"$(var_name)\" was not set. Please set it to the path of todos.db") + StdoutErr(msg) -> ServerErr(msg) route_todos! : Model, Request => Result Response _ route_todos! = \model, req -> when req.method is GET -> - list_todos! model + list_todos!(model) POST -> # Create todo - when task_from_query req.uri is - Ok props -> create_todo! model props - Err InvalidQuery -> text_response 400 "Invalid query string, I expected: ?task=foo&status=bar" + when task_from_query(req.uri) is + Ok(props) -> create_todo!(model, props) + Err(InvalidQuery) -> text_response(400, "Invalid query string, I expected: ?task=foo&status=bar") other_method -> # Not supported - text_response 405 "HTTP method $(Inspect.toStr other_method) is not supported for the URL $(req.uri)" + text_response(405, "HTTP method $(Inspect.to_str(other_method)) is not supported for the URL $(req.uri)") list_todos! : Model => Result Response _ list_todos! = \{ list_todos_stmt } -> result = # TODO: it might be nicer if the decoder was stored with the prepared query instead of defined here. - Sqlite.query_many_prepared! { + Sqlite.query_many_prepared!({ stmt: list_todos_stmt, bindings: [], rows: { Sqlite.decode_record <- - id: Sqlite.i64 "id", - task: Sqlite.str "task", - status: Sqlite.str "status", + id: Sqlite.i64("id"), + task: Sqlite.str("task"), + status: Sqlite.str("status"), }, - } + }) when result is - Ok task -> + Ok(task) -> task - |> List.map encode_task - |> Str.joinWith "," + |> List.map(encode_task) + |> Str.join_with(",") |> \list -> "[$(list)]" - |> Str.toUtf8 + |> Str.to_utf8 |> json_response - Err err -> - err_response err + Err(err) -> + err_response(err) create_todo! : Model, { task : Str, status : Str } => Result Response _ create_todo! = \model, params -> result = - exec_transaction! model \{} -> - try Sqlite.execute_prepared! { + exec_transaction!(model, \{} -> + Sqlite.execute_prepared!({ stmt: model.create_todo_stmt, bindings: [ - { name: ":task", value: String params.task }, - { name: ":status", value: String params.status }, + { name: ":task", value: String(params.task) }, + { name: ":status", value: String(params.status) }, ], - } - Sqlite.query_prepared! { + })? + Sqlite.query_prepared!({ stmt: model.last_created_todo_stmt, bindings: [], row: { Sqlite.decode_record <- - id: Sqlite.i64 "id", - task: Sqlite.str "task", - status: Sqlite.str "status", + id: Sqlite.i64("id"), + task: Sqlite.str("task"), + status: Sqlite.str("status"), }, - } + })) when result is - Ok task -> + Ok(task) -> task |> encode_task - |> Str.toUtf8 + |> Str.to_utf8 |> json_response - Err err -> - err_response err + Err(err) -> + err_response(err) exec_transaction! : Model, ({} => Result ok err) => Result ok [FailedToBeginTransaction, FailedToEndTransaction, FailedToRollbackTransaction, TransactionFailed err] exec_transaction! = \{ begin_stmt, rollback_stmt, end_stmt }, transaction! -> # TODO: create a nicer transaction wrapper - Sqlite.execute_prepared! { + Sqlite.execute_prepared!({ stmt: begin_stmt, bindings: [], - } - |> Result.mapErr \_ -> FailedToBeginTransaction - |> try + }) + |> Result.map_err(\_ -> FailedToBeginTransaction)? end_transaction! = \res -> when res is - Ok v -> - Sqlite.execute_prepared! { + Ok(v) -> + Sqlite.execute_prepared!({ stmt: end_stmt, bindings: [], - } - |> Result.mapErr \_ -> FailedToEndTransaction - |> try - Ok v + }) + |> Result.map_err(\_ -> FailedToEndTransaction)? - Err e -> - Err (TransactionFailed e) + Ok(v) - when transaction! {} |> end_transaction! is - Ok v -> - Ok v + Err(e) -> + Err(TransactionFailed(e)) - Err e -> - Sqlite.execute_prepared! { + when transaction!({}) |> end_transaction! is + Ok(v) -> + Ok(v) + + Err(e) -> + Sqlite.execute_prepared!({ stmt: rollback_stmt, bindings: [], - } - |> Result.mapErr \_ -> FailedToRollbackTransaction - |> try + }) + |> Result.map_err(\_ -> FailedToRollbackTransaction)? - Err e + Err(e) task_from_query : Str -> Result { task : Str, status : Str } [InvalidQuery] task_from_query = \url -> params = url |> Url.from_str |> Url.query_params - when (params |> Dict.get "task", params |> Dict.get "status") is - (Ok task, Ok status) -> Ok { task: Str.replaceEach task "%20" " ", status: Str.replaceEach status "%20" " " } - _ -> Err InvalidQuery + when (params |> Dict.get("task"), params |> Dict.get("status")) is + (Ok(task), Ok(status)) -> Ok({ task: Str.replace_each(task, "%20", " "), status: Str.replace_each(status, "%20", " ") }) + _ -> Err(InvalidQuery) encode_task : { id : I64, task : Str, status : Str } -> Str encode_task = \{ id, task, status } -> # TODO: this should use our json encoder """ - {"id":$(Num.toStr id),"task":"$(task)","status":"$(status)"} + {"id":$(Num.to_str(id)),"task":"$(task)","status":"$(status)"} """ json_response : List U8 -> Result Response [] json_response = \bytes -> - Ok { + Ok({ status: 200, headers: [ { name: "Content-Type", value: "application/json; charset=utf-8" }, ], body: bytes, - } + }) err_response : err -> Result Response * where err implements Inspect err_response = \err -> - byte_response 500 (Str.toUtf8 (Inspect.toStr err)) + byte_response(500, Str.to_utf8(Inspect.to_str(err))) text_response : U16, Str -> Result Response [] text_response = \status, str -> - Ok { + Ok({ status, headers: [ { name: "Content-Type", value: "text/html; charset=utf-8" }, ], - body: Str.toUtf8 str, - } + body: Str.to_utf8(str), + }) byte_response : U16, List U8 -> Result Response * byte_response = \status, bytes -> - Ok { + Ok({ status, headers: [ { name: "Content-Type", value: "text/html; charset=utf-8" }, ], body: bytes, - } + }) log_request! : Request => Result {} [StdoutErr Str] log_request! = \req -> - datetime = Utc.to_iso_8601 (Utc.now! {}) + datetime = Utc.to_iso_8601(Utc.now!({})) - Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)" - |> Result.mapErr \err -> StdoutErr (Inspect.toStr err) + Stdout.line!("$(datetime) $(Inspect.to_str(req.method)) $(req.uri)") + |> Result.map_err(\err -> StdoutErr(Inspect.to_str(err))) read_env_var! : Str => Result Str [EnvVarNotSet Str]_ read_env_var! = \env_var_name -> - Env.var! env_var_name - |> Result.mapErr \_ -> EnvVarNotSet env_var_name + Env.var!(env_var_name) + |> Result.map_err(\_ -> EnvVarNotSet(env_var_name)) prepare_stmt! : Str, Str => Result Sqlite.Stmt [FailedToPrepareQuery _] prepare_stmt! = \path, query -> - Sqlite.prepare! { path, query } - |> Result.mapErr FailedToPrepareQuery + Sqlite.prepare!({ path, query }) + |> Result.map_err(FailedToPrepareQuery) diff --git a/platform/Cmd.roc b/platform/Cmd.roc index 109cd66..d44616a 100644 --- a/platform/Cmd.roc +++ b/platform/Cmd.roc @@ -25,12 +25,12 @@ Output : InternalCmd.Output ## Create a new command to execute the given program in a child process. new : Str -> Cmd new = \program -> - @Cmd { + @Cmd({ program, args: [], envs: [], clear_envs: Bool.false, - } + }) ## Add a single argument to the command. ## ! Shell features like variable subsitition (e.g. `$FOO`), glob patterns (e.g. `*.txt`), ... are not available. @@ -42,8 +42,8 @@ new = \program -> ## ``` ## arg : Cmd, Str -> Cmd -arg = \@Cmd cmd, value -> - @Cmd ({ cmd & args: List.append cmd.args value }) +arg = \@Cmd(cmd), value -> + @Cmd({ cmd & args: List.append(cmd.args, value) }) ## Add multiple arguments to the command. ## ! Shell features like variable subsitition (e.g. `$FOO`), glob patterns (e.g. `*.txt`), ... are not available. @@ -55,8 +55,8 @@ arg = \@Cmd cmd, value -> ## ``` ## args : Cmd, List Str -> Cmd -args = \@Cmd cmd, values -> - @Cmd ({ cmd & args: List.concat cmd.args values }) +args = \@Cmd(cmd), values -> + @Cmd({ cmd & args: List.concat(cmd.args, values) }) ## Add a single environment variable to the command. ## @@ -67,8 +67,8 @@ args = \@Cmd cmd, values -> ## ``` ## env : Cmd, Str, Str -> Cmd -env = \@Cmd cmd, key, value -> - @Cmd ({ cmd & envs: List.concat cmd.envs [key, value] }) +env = \@Cmd(cmd), key, value -> + @Cmd({ cmd & envs: List.concat(cmd.envs, [key, value]) }) ## Add multiple environment variables to the command. ## @@ -79,9 +79,9 @@ env = \@Cmd cmd, key, value -> ## ``` ## envs : Cmd, List (Str, Str) -> Cmd -envs = \@Cmd cmd, key_values -> - values = key_values |> List.joinMap \(key, value) -> [key, value] - @Cmd { cmd & envs: List.concat cmd.envs values } +envs = \@Cmd(cmd), key_values -> + values = key_values |> List.join_map(\(key, value) -> [key, value]) + @Cmd({ cmd & envs: List.concat(cmd.envs, values) }) ## Clear all environment variables, and prevent inheriting from parent, only ## the environment variables provided to command are available to the child. @@ -94,8 +94,8 @@ envs = \@Cmd cmd, key_values -> ## ``` ## clear_envs : Cmd -> Cmd -clear_envs = \@Cmd cmd -> - @Cmd { cmd & clear_envs: Bool.true } +clear_envs = \@Cmd(cmd) -> + @Cmd({ cmd & clear_envs: Bool.true }) ## Execute command and capture stdout and stderr ## @@ -103,17 +103,17 @@ clear_envs = \@Cmd cmd -> ## > to read from the stdin stream will result in the stream immediately closing. ## output! : Cmd => Output -output! = \@Cmd cmd -> - Host.command_output! cmd +output! = \@Cmd(cmd) -> + Host.command_output!(cmd) |> InternalCmd.from_host_output ## Execute command and inherit stdin, stdout and stderr from parent ## status! : Cmd => Result I32 [CmdStatusErr InternalIOErr.IOErr] -status! = \@Cmd cmd -> - Host.command_status! cmd - |> Result.mapErr InternalIOErr.handle_err - |> Result.mapErr CmdStatusErr +status! = \@Cmd(cmd) -> + Host.command_status!(cmd) + |> Result.map_err(InternalIOErr.handle_err) + |> Result.map_err(CmdStatusErr) ## Execute command and inherit stdin, stdout and stderr from parent ## @@ -124,11 +124,11 @@ status! = \@Cmd cmd -> exec! : Str, List Str => Result {} [CmdStatusErr InternalIOErr.IOErr] exec! = \program, arguments -> exit_code = - new program - |> args arguments + new(program) + |> args(arguments) |> status!? if exit_code == 0i32 then - Ok {} + Ok({}) else - Err (CmdStatusErr (Other "Non-zero exit code $(Num.toStr exit_code)")) + Err(CmdStatusErr(Other("Non-zero exit code $(Num.to_str(exit_code))"))) diff --git a/platform/Dir.roc b/platform/Dir.roc index 5fd9a9b..14e6411 100644 --- a/platform/Dir.roc +++ b/platform/Dir.roc @@ -26,7 +26,7 @@ DirEntry : Path.DirEntry ## > [Path.list_dir!] does the same thing, except it takes a [Path] instead of a [Str]. list! : Str => Result (List Path) [DirErr IOErr] list! = \path -> - Path.list_dir! (Path.from_str path) + Path.list_dir!(Path.from_str(path)) ## Deletes a directory if it's empty ## @@ -39,7 +39,7 @@ list! = \path -> ## > [Path.delete_empty!] does the same thing, except it takes a [Path] instead of a [Str]. delete_empty! : Str => Result {} [DirErr IOErr] delete_empty! = \path -> - Path.delete_empty! (Path.from_str path) + Path.delete_empty!(Path.from_str(path)) ## Recursively deletes the directory as well as all files and directories ## inside it. @@ -53,7 +53,7 @@ delete_empty! = \path -> ## > [Path.delete_all!] does the same thing, except it takes a [Path] instead of a [Str]. delete_all! : Str => Result {} [DirErr IOErr] delete_all! = \path -> - Path.delete_all! (Path.from_str path) + Path.delete_all!(Path.from_str(path)) ## Creates a directory ## @@ -65,7 +65,7 @@ delete_all! = \path -> ## > [Path.create_dir!] does the same thing, except it takes a [Path] instead of a [Str]. create! : Str => Result {} [DirErr IOErr] create! = \path -> - Path.create_dir! (Path.from_str path) + Path.create_dir!(Path.from_str(path)) ## Creates a directory recursively adding any missing parent directories. ## @@ -76,4 +76,4 @@ create! = \path -> ## > [Path.create_all!] does the same thing, except it takes a [Path] instead of a [Str]. create_all! : Str => Result {} [DirErr IOErr] create_all! = \path -> - Path.create_all! (Path.from_str path) + Path.create_all!(Path.from_str(path)) diff --git a/platform/Env.roc b/platform/Env.roc index 14c015f..00aeaa0 100644 --- a/platform/Env.roc +++ b/platform/Env.roc @@ -18,27 +18,27 @@ import Host ## from the environment. File operations on relative [Path]s are relative to this directory. cwd! : {} => Result Path [CwdUnavailable] cwd! = \{} -> - bytes = Host.cwd! {} |> Result.withDefault [] + bytes = Host.cwd!({}) |> Result.with_default([]) - if List.isEmpty bytes then - Err CwdUnavailable + if List.is_empty(bytes) then + Err(CwdUnavailable) else - Ok (InternalPath.from_arbitrary_bytes bytes) + Ok(InternalPath.from_arbitrary_bytes(bytes)) ## Sets the [current working directory](https://en.wikipedia.org/wiki/Working_directory) ## in the environment. After changing it, file operations on relative [Path]s will be relative ## to this directory. set_cwd! : Path => Result {} [InvalidCwd] set_cwd! = \path -> - Host.set_cwd! (InternalPath.to_bytes path) - |> Result.mapErr \{} -> InvalidCwd + Host.set_cwd!(InternalPath.to_bytes(path)) + |> Result.map_err(\{} -> InvalidCwd) ## Gets the path to the currently-running executable. exe_path! : {} => Result Path [ExePathUnavailable] exe_path! = \{} -> - when Host.exe_path! {} is - Ok bytes -> Ok (InternalPath.from_os_bytes bytes) - Err {} -> Err ExePathUnavailable + when Host.exe_path!({}) is + Ok(bytes) -> Ok(InternalPath.from_os_bytes(bytes)) + Err({}) -> Err(ExePathUnavailable) ## Reads the given environment variable. ## @@ -46,8 +46,8 @@ exe_path! = \{} -> ## [Unicode replacement character](https://unicode.org/glossary/#replacement_character) ('�'). var! : Str => Result Str [VarNotFound] var! = \name -> - Host.env_var! name - |> Result.mapErr \{} -> VarNotFound + Host.env_var!(name) + |> Result.map_err(\{} -> VarNotFound) ## Reads the given environment variable and attempts to decode it. ## @@ -76,12 +76,12 @@ var! = \name -> ## decode! : Str => Result val [VarNotFound, DecodeErr DecodeError] where val implements Decoding decode! = \name -> - when Host.env_var! name is - Err {} -> Err VarNotFound - Ok var_str -> - Str.toUtf8 var_str - |> Decode.fromBytes (EnvDecoding.format {}) - |> Result.mapErr (\_ -> DecodeErr TooShort) + when Host.env_var!(name) is + Err({}) -> Err(VarNotFound) + Ok(var_str) -> + Str.to_utf8(var_str) + |> Decode.from_bytes(EnvDecoding.format({})) + |> Result.map_err(\_ -> DecodeErr(TooShort)) ## Reads all the process's environment variables into a [Dict]. ## @@ -89,8 +89,8 @@ decode! = \name -> ## will be used in place of any parts of keys or values that are invalid Unicode. dict! : {} => Dict Str Str dict! = \{} -> - Host.env_dict! {} - |> Dict.fromList + Host.env_dict!({}) + |> Dict.from_list # ## Walks over the process's environment variables as key-value arguments to the walking function. # ## @@ -137,7 +137,7 @@ OS : [LINUX, MACOS, WINDOWS, OTHER Str] platform! : {} => { arch : ARCH, os : OS } platform! = \{} -> - from_rust = Host.current_arch_os! {} + from_rust = Host.current_arch_os!({}) arch = when from_rust.arch is @@ -145,14 +145,14 @@ platform! = \{} -> "x86_64" -> X64 "arm" -> ARM "aarch64" -> AARCH64 - _ -> OTHER from_rust.arch + _ -> OTHER(from_rust.arch) os = when from_rust.os is "linux" -> LINUX "macos" -> MACOS "windows" -> WINDOWS - _ -> OTHER from_rust.os + _ -> OTHER(from_rust.os) { arch, os } @@ -167,5 +167,5 @@ platform! = \{} -> ## temp_dir! : {} => Path temp_dir! = \{} -> - Host.temp_dir! {} + Host.temp_dir!({}) |> InternalPath.from_os_bytes diff --git a/platform/EnvDecoding.roc b/platform/EnvDecoding.roc index 0ca77b5..f11ae4a 100644 --- a/platform/EnvDecoding.roc +++ b/platform/EnvDecoding.roc @@ -27,82 +27,85 @@ EnvFormat := {} implements [ ] format : {} -> EnvFormat -format = \{} -> @EnvFormat {} +format = \{} -> @EnvFormat({}) decode_bytes_to_num = \bytes, transformer -> - when Str.fromUtf8 bytes is - Ok s -> - when transformer s is - Ok n -> { result: Ok n, rest: [] } - Err _ -> { result: Err TooShort, rest: bytes } + when Str.from_utf8(bytes) is + Ok(s) -> + when transformer(s) is + Ok(n) -> { result: Ok(n), rest: [] } + Err(_) -> { result: Err(TooShort), rest: bytes } - Err _ -> { result: Err TooShort, rest: bytes } + Err(_) -> { result: Err(TooShort), rest: bytes } -env_u8 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toU8 -env_u16 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toU16 -env_u32 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toU32 -env_u64 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toU64 -env_u128 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toU128 -env_i8 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toI8 -env_i16 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toI16 -env_i32 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toI32 -env_i64 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toI64 -env_i128 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toI128 -env_f32 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toF32 -env_f64 = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toF64 -env_dec = Decode.custom \bytes, @EnvFormat {} -> decode_bytes_to_num bytes Str.toDec +env_u8 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_u8)) +env_u16 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_u16)) +env_u32 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_u32)) +env_u64 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_u64)) +env_u128 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_u128)) +env_i8 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_i8)) +env_i16 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_i16)) +env_i32 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_i32)) +env_i64 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_i64)) +env_i128 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_i128)) +env_f32 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_f32)) +env_f64 = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_f64)) +env_dec = Decode.custom(\bytes, @EnvFormat({}) -> decode_bytes_to_num(bytes, Str.to_dec)) -env_bool = Decode.custom \bytes, @EnvFormat {} -> - when Str.fromUtf8 bytes is - Ok "true" -> { result: Ok Bool.true, rest: [] } - Ok "false" -> { result: Ok Bool.false, rest: [] } - _ -> { result: Err TooShort, rest: bytes } +env_bool = Decode.custom(\bytes, @EnvFormat({}) -> + when Str.from_utf8(bytes) is + Ok("true") -> { result: Ok(Bool.true), rest: [] } + Ok("false") -> { result: Ok(Bool.false), rest: [] } + _ -> { result: Err(TooShort), rest: bytes }) -env_string = Decode.custom \bytes, @EnvFormat {} -> - when Str.fromUtf8 bytes is - Ok s -> { result: Ok s, rest: [] } - Err _ -> { result: Err TooShort, rest: bytes } +env_string = Decode.custom(\bytes, @EnvFormat({}) -> + when Str.from_utf8(bytes) is + Ok(s) -> { result: Ok(s), rest: [] } + Err(_) -> { result: Err(TooShort), rest: bytes }) -env_list = \decode_elem -> Decode.custom \bytes, @EnvFormat {} -> - # Per our supported methods of decoding, this is either a list of strings or - # a list of numbers; in either case, the list of bytes must be Utf-8 - # decodable. So just parse it as a list of strings and pass each chunk to - # the element decoder. By construction, our element decoders expect to parse - # a whole list of bytes anyway. - decode_elems = \all_bytes, accum -> - { to_parse, remainder } = - when List.splitFirst all_bytes (Num.toU8 ',') is - Ok { before, after } -> - { to_parse: before, remainder: Some after } +env_list = \decode_elem -> + Decode.custom(\bytes, @EnvFormat({}) -> + # Per our supported methods of decoding, this is either a list of strings or + # a list of numbers; in either case, the list of bytes must be Utf-8 + # decodable. So just parse it as a list of strings and pass each chunk to + # the element decoder. By construction, our element decoders expect to parse + # a whole list of bytes anyway. + decode_elems = \all_bytes, accum -> + { to_parse, remainder } = + when List.split_first(all_bytes, Num.to_u8(',')) is + Ok({ before, after }) -> + { to_parse: before, remainder: Some(after) } - Err NotFound -> - { to_parse: all_bytes, remainder: None } + Err(NotFound) -> + { to_parse: all_bytes, remainder: None } - when Decode.decodeWith to_parse decode_elem (@EnvFormat {}) is - { result, rest } -> - when result is - Ok val -> - when remainder is - Some rest_bytes -> decode_elems rest_bytes (List.append accum val) - None -> Done (List.append accum val) + when Decode.decode_with(to_parse, decode_elem, @EnvFormat({})) is + { result, rest } -> + when result is + Ok(val) -> + when remainder is + Some(rest_bytes) -> decode_elems(rest_bytes, List.append(accum, val)) + None -> Done(List.append(accum, val)) - Err e -> Errored e rest + Err(e) -> Errored(e, rest) - when decode_elems bytes [] is - Errored e rest -> { result: Err e, rest } - Done vals -> - { result: Ok vals, rest: [] } + when decode_elems(bytes, []) is + Errored(e, rest) -> { result: Err(e), rest } + Done(vals) -> + { result: Ok(vals), rest: [] }) # TODO: we must currently annotate the arrows here so that the lambda sets are # exercised, and the solver can find an ambient lambda set for the # specialization. env_record : _, (_, _ -> [Keep (Decoder _ _), Skip]), (_, _ -> _) -> Decoder _ _ -env_record = \_initialState, _stepField, _finalizer -> Decode.custom \bytes, @EnvFormat {} -> - { result: Err TooShort, rest: bytes } +env_record = \_initialState, _stepField, _finalizer -> + Decode.custom(\bytes, @EnvFormat({}) -> + { result: Err(TooShort), rest: bytes }) # TODO: we must currently annotate the arrows here so that the lambda sets are # exercised, and the solver can find an ambient lambda set for the # specialization. env_tuple : _, (_, _ -> [Next (Decoder _ _), TooLong]), (_ -> _) -> Decoder _ _ -env_tuple = \_initialState, _stepElem, _finalizer -> Decode.custom \bytes, @EnvFormat {} -> - { result: Err TooShort, rest: bytes } +env_tuple = \_initialState, _stepElem, _finalizer -> + Decode.custom(\bytes, @EnvFormat({}) -> + { result: Err(TooShort), rest: bytes }) diff --git a/platform/File.roc b/platform/File.roc index 5d972c8..dc76293 100644 --- a/platform/File.roc +++ b/platform/File.roc @@ -65,7 +65,7 @@ IOErr : InternalIOErr.IOErr ## > [Path.write!] does the same thing, except it takes a [Path] instead of a [Str]. write! : val, Str, fmt => Result {} [FileWriteErr Path IOErr] where val implements Encoding, fmt implements EncoderFormatting write! = \val, path, fmt -> - Path.write! val (Path.from_str path) fmt + Path.write!(val, Path.from_str(path), fmt) ## Writes bytes to a file. ## @@ -81,7 +81,7 @@ write! = \val, path, fmt -> ## > [Path.write_bytes!] does the same thing, except it takes a [Path] instead of a [Str]. write_bytes! : List U8, Str => Result {} [FileWriteErr Path IOErr] write_bytes! = \bytes, path -> - Path.write_bytes! bytes (Path.from_str path) + Path.write_bytes!(bytes, Path.from_str(path)) ## Writes a [Str] to a file, encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8). ## @@ -97,7 +97,7 @@ write_bytes! = \bytes, path -> ## > [Path.write_utf8!] does the same thing, except it takes a [Path] instead of a [Str]. write_utf8! : Str, Str => Result {} [FileWriteErr Path IOErr] write_utf8! = \str, path -> - Path.write_utf8! str (Path.from_str path) + Path.write_utf8!(str, Path.from_str(path)) ## Deletes a file from the filesystem. ## @@ -121,7 +121,7 @@ write_utf8! = \str, path -> ## > [Path.delete!] does the same thing, except it takes a [Path] instead of a [Str]. delete! : Str => Result {} [FileWriteErr Path IOErr] delete! = \path -> - Path.delete! (Path.from_str path) + Path.delete!(Path.from_str(path)) ## Reads all the bytes in a file. ## @@ -137,7 +137,7 @@ delete! = \path -> ## > [Path.read_bytes!] does the same thing, except it takes a [Path] instead of a [Str]. read_bytes! : Str => Result (List U8) [FileReadErr Path IOErr] read_bytes! = \path -> - Path.read_bytes! (Path.from_str path) + Path.read_bytes!(Path.from_str(path)) ## Reads a [Str] from a file containing [UTF-8](https://en.wikipedia.org/wiki/UTF-8)-encoded text. ## @@ -154,7 +154,7 @@ read_bytes! = \path -> ## > [Path.read_utf8!] does the same thing, except it takes a [Path] instead of a [Str]. read_utf8! : Str => Result Str [FileReadErr Path IOErr, FileReadUtf8Err Path _] read_utf8! = \path -> - Path.read_utf8! (Path.from_str path) + Path.read_utf8!(Path.from_str(path)) # read : Str, fmt => Result contents [FileReadErr Path ReadErr, FileReadDecodingFailed] where contents implements Decoding, fmt implements DecoderFormatting # read = \path, fmt -> @@ -170,7 +170,7 @@ read_utf8! = \path -> ## > [Path.hard_link!] does the same thing, except it takes a [Path] instead of a [Str]. hard_link! : Str => Result {} [LinkErr IOErr] hard_link! = \path -> - Path.hard_link! (Path.from_str path) + Path.hard_link!(Path.from_str(path)) ## Returns True if the path exists on disk and is pointing at a directory. ## Returns False if the path exists and it is not a directory. If the path does not exist, @@ -181,7 +181,7 @@ hard_link! = \path -> ## > [Path.is_dir!] does the same thing, except it takes a [Path] instead of a [Str]. is_dir! : Str => Result Bool [PathErr IOErr] is_dir! = \path -> - Path.is_dir! (Path.from_str path) + Path.is_dir!(Path.from_str(path)) ## Returns True if the path exists on disk and is pointing at a regular file. ## Returns False if the path exists and it is not a file. If the path does not exist, @@ -192,7 +192,7 @@ is_dir! = \path -> ## > [Path.is_file!] does the same thing, except it takes a [Path] instead of a [Str]. is_file! : Str => Result Bool [PathErr IOErr] is_file! = \path -> - Path.is_file! (Path.from_str path) + Path.is_file!(Path.from_str(path)) ## Returns True if the path exists on disk and is pointing at a symbolic link. ## Returns False if the path exists and it is not a symbolic link. If the path does not exist, @@ -203,7 +203,7 @@ is_file! = \path -> ## > [Path.is_sym_link!] does the same thing, except it takes a [Path] instead of a [Str]. is_sym_link! : Str => Result Bool [PathErr IOErr] is_sym_link! = \path -> - Path.is_sym_link! (Path.from_str path) + Path.is_sym_link!(Path.from_str(path)) ## Return the type of the path if the path exists on disk. ## This uses [rust's std::path::is_symlink](https://doc.rust-lang.org/std/path/struct.Path.html#method.is_symlink). @@ -211,7 +211,7 @@ is_sym_link! = \path -> ## > [Path.type!] does the same thing, except it takes a [Path] instead of a [Str]. type! : Str => Result [IsFile, IsDir, IsSymLink] [PathErr IOErr] type! = \path -> - Path.type! (Path.from_str path) + Path.type!(Path.from_str(path)) Reader := { reader : Host.FileReader, path : Path } @@ -223,12 +223,12 @@ Reader := { reader : Host.FileReader, path : Path } ## Use [read_utf8!] if you want to get the entire file contents at once. open_reader! : Str => Result Reader [GetFileReadErr Path IOErr] open_reader! = \path_str -> - path = Path.from_str path_str + path = Path.from_str(path_str) # 0 means with default capacity - Host.file_reader! (Str.toUtf8 path_str) 0 - |> Result.mapErr \err -> GetFileReadErr path (InternalIOErr.handle_err err) - |> Result.map \reader -> @Reader { reader, path } + Host.file_reader!(Str.to_utf8(path_str), 0) + |> Result.map_err(\err -> GetFileReadErr(path, InternalIOErr.handle_err(err))) + |> Result.map(\reader -> @Reader({ reader, path })) ## Try to open a `File.Reader` for buffered (= part by part) reading given a path string. ## The buffer will be created with the specified capacity. @@ -239,11 +239,11 @@ open_reader! = \path_str -> ## Use [read_utf8!] if you want to get the entire file contents at once. open_reader_with_capacity! : Str, U64 => Result Reader [GetFileReadErr Path IOErr] open_reader_with_capacity! = \path_str, capacity -> - path = Path.from_str path_str + path = Path.from_str(path_str) - Host.file_reader! (Str.toUtf8 path_str) capacity - |> Result.mapErr \err -> GetFileReadErr path (InternalIOErr.handle_err err) - |> Result.map \reader -> @Reader { reader, path } + Host.file_reader!(Str.to_utf8(path_str), capacity) + |> Result.map_err(\err -> GetFileReadErr(path, InternalIOErr.handle_err(err))) + |> Result.map(\reader -> @Reader({ reader, path })) ## Try to read a line from a file given a Reader. ## The line will be provided as the list of bytes (`List U8`) until a newline (`0xA` byte). @@ -254,6 +254,6 @@ open_reader_with_capacity! = \path_str, capacity -> ## ## Use [read_utf8!] if you want to get the entire file contents at once. read_line! : Reader => Result (List U8) [FileReadErr Path IOErr] -read_line! = \@Reader { reader, path } -> - Host.file_read_line! reader - |> Result.mapErr \err -> FileReadErr path (InternalIOErr.handle_err err) +read_line! = \@Reader({ reader, path }) -> + Host.file_read_line!(reader) + |> Result.map_err(\err -> FileReadErr(path, InternalIOErr.handle_err(err))) diff --git a/platform/FileMetadata.roc b/platform/FileMetadata.roc index b66dbc1..a78b4a9 100644 --- a/platform/FileMetadata.roc +++ b/platform/FileMetadata.roc @@ -20,19 +20,19 @@ FileMetadata := { ## Returns the number of bytes in the associated file. bytes : FileMetadata -> U64 -bytes = \@FileMetadata info -> info.bytes +bytes = \@FileMetadata(info) -> info.bytes ## Returns [Bool.true] if the associated file is read-only. is_readonly : FileMetadata -> Bool -is_readonly = \@FileMetadata info -> info.is_readonly +is_readonly = \@FileMetadata(info) -> info.is_readonly ## Returns the type of the associated file. type : FileMetadata -> [File, Dir, Symlink] -type = \@FileMetadata info -> info.type +type = \@FileMetadata(info) -> info.type ## Returns the mode of the associated file. mode : FileMetadata -> [Unix U32, NonUnix] -mode = \@FileMetadata info -> info.mode +mode = \@FileMetadata(info) -> info.mode # TODO need to create a Time module and return something like Time.Utc here. # lastModified : FileMetadata -> Utc diff --git a/platform/Http.roc b/platform/Http.roc index 696b7ff..0fec9c2 100644 --- a/platform/Http.roc +++ b/platform/Http.roc @@ -82,15 +82,15 @@ send! = \request -> ## ``` get! : Str, fmt => Result body [HttpDecodingFailed] where body implements Decoding, fmt implements DecoderFormatting get! = \uri, fmt -> - response = send! { default_request & uri } + response = send!({ default_request & uri }) - Decode.fromBytes response.body fmt - |> Result.mapErr \_ -> HttpDecodingFailed + Decode.from_bytes(response.body, fmt) + |> Result.map_err(\_ -> HttpDecodingFailed) get_utf8! : Str => Result Str [BadBody Str] get_utf8! = \uri -> - response = send! { default_request & uri } + response = send!({ default_request & uri }) response.body - |> Str.fromUtf8 - |> Result.mapErr \_ -> BadBody "Invalid UTF-8" + |> Str.from_utf8 + |> Result.map_err(\_ -> BadBody("Invalid UTF-8")) diff --git a/platform/InternalCmd.roc b/platform/InternalCmd.roc index 2fe8f9b..7be8a52 100644 --- a/platform/InternalCmd.roc +++ b/platform/InternalCmd.roc @@ -22,7 +22,7 @@ Output : { from_host_output : OutputFromHost -> Output from_host_output = \{ status, stdout, stderr } -> { - status: Result.mapErr status InternalIOErr.handle_err, + status: Result.map_err(status, InternalIOErr.handle_err), stdout, stderr, } diff --git a/platform/InternalDateTime.roc b/platform/InternalDateTime.roc index 0b219e7..3a8e403 100644 --- a/platform/InternalDateTime.roc +++ b/platform/InternalDateTime.roc @@ -8,18 +8,18 @@ DateTime : { year : I128, month : I128, day : I128, hours : I128, minutes : I128 to_iso_8601 : DateTime -> Str to_iso_8601 = \{ year, month, day, hours, minutes, seconds } -> - year_str = year_with_padded_zeros year - month_str = month_with_padded_zeros month - day_str = day_with_padded_zeros day - hour_str = hours_with_padded_zeros hours - minute_str = minutes_with_padded_zeros minutes - seconds_str = seconds_with_padded_zeros seconds + year_str = year_with_padded_zeros(year) + month_str = month_with_padded_zeros(month) + day_str = day_with_padded_zeros(day) + hour_str = hours_with_padded_zeros(hours) + minute_str = minutes_with_padded_zeros(minutes) + seconds_str = seconds_with_padded_zeros(seconds) "$(year_str)-$(month_str)-$(day_str)T$(hour_str):$(minute_str):$(seconds_str)Z" year_with_padded_zeros : I128 -> Str year_with_padded_zeros = \year -> - year_str = Num.toStr year + year_str = Num.to_str(year) if year < 10 then "000$(year_str)" else if year < 100 then @@ -31,7 +31,7 @@ year_with_padded_zeros = \year -> month_with_padded_zeros : I128 -> Str month_with_padded_zeros = \month -> - month_str = Num.toStr month + month_str = Num.to_str(month) if month < 10 then "0$(month_str)" else @@ -59,37 +59,37 @@ is_leap_year = \year -> (year % 400 == 0) # expecpt when also divisible by 400 ) -expect is_leap_year 2000 -expect is_leap_year 2012 -expect !(is_leap_year 1900) -expect !(is_leap_year 2015) -expect List.map [2023, 1988, 1992, 1996] is_leap_year == [Bool.false, Bool.true, Bool.true, Bool.true] -expect List.map [1700, 1800, 1900, 2100, 2200, 2300, 2500, 2600] is_leap_year == [Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false] +expect is_leap_year(2000) +expect is_leap_year(2012) +expect !(is_leap_year(1900)) +expect !(is_leap_year(2015)) +expect List.map([2023, 1988, 1992, 1996], is_leap_year) == [Bool.false, Bool.true, Bool.true, Bool.true] +expect List.map([1700, 1800, 1900, 2100, 2200, 2300, 2500, 2600], is_leap_year) == [Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false] days_in_month : I128, I128 -> I128 days_in_month = \year, month -> - if List.contains [1, 3, 5, 7, 8, 10, 12] month then + if List.contains([1, 3, 5, 7, 8, 10, 12], month) then 31 - else if List.contains [4, 6, 9, 11] month then + else if List.contains([4, 6, 9, 11], month) then 30 else if month == 2 then - (if is_leap_year year then 29 else 28) + (if is_leap_year(year) then 29 else 28) else 0 -expect days_in_month 2023 1 == 31 # January -expect days_in_month 2023 2 == 28 # February -expect days_in_month 1996 2 == 29 # February in a leap year -expect days_in_month 2023 3 == 31 # March -expect days_in_month 2023 4 == 30 # April -expect days_in_month 2023 5 == 31 # May -expect days_in_month 2023 6 == 30 # June -expect days_in_month 2023 7 == 31 # July -expect days_in_month 2023 8 == 31 # August -expect days_in_month 2023 9 == 30 # September -expect days_in_month 2023 10 == 31 # October -expect days_in_month 2023 11 == 30 # November -expect days_in_month 2023 12 == 31 # December +expect days_in_month(2023, 1) == 31 # January +expect days_in_month(2023, 2) == 28 # February +expect days_in_month(1996, 2) == 29 # February in a leap year +expect days_in_month(2023, 3) == 31 # March +expect days_in_month(2023, 4) == 30 # April +expect days_in_month(2023, 5) == 31 # May +expect days_in_month(2023, 6) == 30 # June +expect days_in_month(2023, 7) == 31 # July +expect days_in_month(2023, 8) == 31 # August +expect days_in_month(2023, 9) == 30 # September +expect days_in_month(2023, 10) == 31 # October +expect days_in_month(2023, 11) == 30 # November +expect days_in_month(2023, 12) == 31 # December epoch_millis_to_datetime : I128 -> DateTime epoch_millis_to_datetime = \millis -> @@ -100,56 +100,61 @@ epoch_millis_to_datetime = \millis -> month = 1 year = 1970 - epoch_millis_to_datetimeHelp { + epoch_millis_to_datetimeHelp({ year, month, day, hours: hours % 24, minutes: minutes % 60, seconds: seconds % 60, - } + }) epoch_millis_to_datetimeHelp : DateTime -> DateTime epoch_millis_to_datetimeHelp = \current -> - count_days_in_month = days_in_month current.year current.month + count_days_in_month = days_in_month(current.year, current.month) count_days_in_prev_month = if current.month == 1 then - days_in_month (current.year - 1) 12 + days_in_month((current.year - 1), 12) else - days_in_month current.year (current.month - 1) + days_in_month(current.year, (current.month - 1)) if current.day < 1 then - epoch_millis_to_datetimeHelp + epoch_millis_to_datetimeHelp( { current & year: if current.month == 1 then current.year - 1 else current.year, month: if current.month == 1 then 12 else current.month - 1, day: current.day + count_days_in_prev_month, - } + }, + ) else if current.hours < 0 then - epoch_millis_to_datetimeHelp + epoch_millis_to_datetimeHelp( { current & day: current.day - 1, hours: current.hours + 24, - } + }, + ) else if current.minutes < 0 then - epoch_millis_to_datetimeHelp + epoch_millis_to_datetimeHelp( { current & hours: current.hours - 1, minutes: current.minutes + 60, - } + }, + ) else if current.seconds < 0 then - epoch_millis_to_datetimeHelp + epoch_millis_to_datetimeHelp( { current & minutes: current.minutes - 1, seconds: current.seconds + 60, - } + }, + ) else if current.day > count_days_in_month then - epoch_millis_to_datetimeHelp + epoch_millis_to_datetimeHelp( { current & year: if current.month == 12 then current.year + 1 else current.year, month: if current.month == 12 then 1 else current.month + 1, day: current.day - count_days_in_month, - } + }, + ) else current diff --git a/platform/InternalHttp.roc b/platform/InternalHttp.roc index 3936ee7..e994ca0 100644 --- a/platform/InternalHttp.roc +++ b/platform/InternalHttp.roc @@ -64,12 +64,12 @@ to_host_response = \{ status, headers, body } -> { to_host_request : Request -> RequestToAndFromHost to_host_request = \{ method, headers, uri, body, timeout_ms } -> { - method: to_host_method method, - method_ext: to_host_method_ext method, + method: to_host_method(method), + method_ext: to_host_method_ext(method), headers, uri, body, - timeout_ms: to_host_timeout timeout_ms, + timeout_ms: to_host_timeout(timeout_ms), } to_host_method : Method -> _ @@ -84,27 +84,27 @@ to_host_method = \method -> TRACE -> 9 CONNECT -> 0 PATCH -> 6 - EXTENSION _ -> 2 + EXTENSION(_) -> 2 to_host_method_ext : Method -> Str to_host_method_ext = \method -> when method is - EXTENSION ext -> ext + EXTENSION(ext) -> ext _ -> "" to_host_timeout : _ -> U64 to_host_timeout = \timeout -> when timeout is - TimeoutMilliseconds ms -> ms + TimeoutMilliseconds(ms) -> ms NoTimeout -> 0 from_host_request : RequestToAndFromHost -> Request from_host_request = \{ method, method_ext, headers, uri, body, timeout_ms } -> { - method: from_host_method method method_ext, + method: from_host_method(method, method_ext), headers, uri, body, - timeout_ms: from_host_timeout timeout_ms, + timeout_ms: from_host_timeout(timeout_ms), } from_host_method : U64, Str -> Method @@ -119,17 +119,17 @@ from_host_method = \tag, ext -> 9 -> TRACE 0 -> CONNECT 6 -> PATCH - 2 -> EXTENSION ext - _ -> crash "invalid tag from host" + 2 -> EXTENSION(ext) + _ -> crash("invalid tag from host") from_host_timeout : U64 -> [TimeoutMilliseconds U64, NoTimeout] from_host_timeout = \timeout -> when timeout is 0 -> NoTimeout - _ -> TimeoutMilliseconds timeout + _ -> TimeoutMilliseconds(timeout) -expect from_host_timeout 0 == NoTimeout -expect from_host_timeout 1 == TimeoutMilliseconds 1 +expect from_host_timeout(0) == NoTimeout +expect from_host_timeout(1) == TimeoutMilliseconds(1) from_host_response : ResponseToAndFromHost -> Response from_host_response = \{ status, headers, body } -> { diff --git a/platform/InternalIOErr.roc b/platform/InternalIOErr.roc index ef95d76..5d5f20e 100644 --- a/platform/InternalIOErr.roc +++ b/platform/InternalIOErr.roc @@ -55,4 +55,4 @@ handle_err = \{ tag, msg } -> Interrupted -> Interrupted Unsupported -> Unsupported OutOfMemory -> OutOfMemory - Other | EndOfFile -> Other msg + Other | EndOfFile -> Other(msg) diff --git a/platform/InternalPath.roc b/platform/InternalPath.roc index 258fc8b..05779bf 100644 --- a/platform/InternalPath.roc +++ b/platform/InternalPath.roc @@ -57,22 +57,22 @@ wrap : UnwrappedPath -> InternalPath wrap = @InternalPath unwrap : InternalPath -> UnwrappedPath -unwrap = \@InternalPath raw -> raw +unwrap = \@InternalPath(raw) -> raw ## 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 : InternalPath -> List U8 -to_bytes = \@InternalPath path -> +to_bytes = \@InternalPath(path) -> when path is - FromOperatingSystem bytes -> bytes - ArbitraryBytes bytes -> bytes - FromStr str -> Str.toUtf8 str + FromOperatingSystem(bytes) -> bytes + ArbitraryBytes(bytes) -> bytes + FromStr(str) -> Str.to_utf8(str) from_arbitrary_bytes : List U8 -> InternalPath from_arbitrary_bytes = \bytes -> - @InternalPath (ArbitraryBytes bytes) + @InternalPath(ArbitraryBytes(bytes)) from_os_bytes : List U8 -> InternalPath from_os_bytes = \bytes -> - @InternalPath (FromOperatingSystem bytes) + @InternalPath(FromOperatingSystem(bytes)) diff --git a/platform/MultipartFormData.roc b/platform/MultipartFormData.roc index 585a226..6d80d69 100644 --- a/platform/MultipartFormData.roc +++ b/platform/MultipartFormData.roc @@ -54,140 +54,141 @@ doubledash = ['-', '-'] ## ``` ## parse_content_f : { upper : List U8, lower : List U8 } -> (List U8 -> Result { value : List U8, rest : List U8 } _) -parse_content_f = \{ upper, lower } -> \bytes -> - - toSearchUpper = List.concat newline upper - toSearchLower = List.concat newline lower - searchLength = List.len toSearchUpper - afterSearch = List.sublist bytes { start: searchLength, len: Num.maxU64 } - - if - List.startsWith bytes toSearchUpper - || List.startsWith bytes toSearchLower - then - nextLineStart = afterSearch |> List.findFirstIndex? \b -> b == '\r' - - Ok { - value: List.sublist afterSearch { start: 0, len: nextLineStart }, - rest: List.sublist afterSearch { start: nextLineStart, len: Num.maxU64 }, - } - else - Err ExpectedContent - -parseContentDispositionF = parse_content_f { - upper: Str.toUtf8 "Content-Disposition:", - lower: Str.toUtf8 "content-disposition:", -} +parse_content_f = \{ upper, lower } -> + \bytes -> + + to_search_upper = List.concat(newline, upper) + to_search_lower = List.concat(newline, lower) + search_length = List.len(to_search_upper) + after_search = List.sublist(bytes, { start: search_length, len: Num.max_u64 }) + + if + List.starts_with(bytes, to_search_upper) + || List.starts_with(bytes, to_search_lower) + then + next_line_start = after_search |> List.find_first_index?(\b -> b == '\r') + + Ok({ + value: List.sublist(after_search, { start: 0, len: next_line_start }), + rest: List.sublist(after_search, { start: next_line_start, len: Num.max_u64 }), + }) + else + Err(ExpectedContent) + +parse_content_disposition_f = parse_content_f({ + upper: Str.to_utf8("Content-Disposition:"), + lower: Str.to_utf8("content-disposition:"), +}) expect - input = Str.toUtf8 "\r\nContent-Disposition: form-data; name=\"sometext\"\r\nSome text here..." - actual = parseContentDispositionF input - expected = Ok { - value: Str.toUtf8 " form-data; name=\"sometext\"", - rest: Str.toUtf8 "\r\nSome text here...", - } + input = Str.to_utf8("\r\nContent-Disposition: form-data; name=\"sometext\"\r\nSome text here...") + actual = parse_content_disposition_f(input) + expected = Ok({ + value: Str.to_utf8(" form-data; name=\"sometext\""), + rest: Str.to_utf8("\r\nSome text here..."), + }) actual == expected -parseContentTypeF = parse_content_f { - upper: Str.toUtf8 "Content-Type:", - lower: Str.toUtf8 "content-type:", -} +parse_content_type_f = parse_content_f({ + upper: Str.to_utf8("Content-Type:"), + lower: Str.to_utf8("content-type:"), +}) expect - input = Str.toUtf8 "\r\ncontent-type: multipart/mixed; boundary=abcde\r\nSome text here..." - actual = parseContentTypeF input - expected = Ok { - value: Str.toUtf8 " multipart/mixed; boundary=abcde", - rest: Str.toUtf8 "\r\nSome text here...", - } + input = Str.to_utf8("\r\ncontent-type: multipart/mixed; boundary=abcde\r\nSome text here...") + actual = parse_content_type_f(input) + expected = Ok({ + value: Str.to_utf8(" multipart/mixed; boundary=abcde"), + rest: Str.to_utf8("\r\nSome text here..."), + }) actual == expected -parseContentTransferEncodingF = parse_content_f { - upper: Str.toUtf8 "Content-Transfer-Encoding:", - lower: Str.toUtf8 "content-transfer-encoding:", -} +parse_content_transfer_encoding_f = parse_content_f({ + upper: Str.to_utf8("Content-Transfer-Encoding:"), + lower: Str.to_utf8("content-transfer-encoding:"), +}) expect - input = Str.toUtf8 "\r\nContent-Transfer-Encoding: binary\r\nSome text here..." - actual = parseContentTransferEncodingF input - expected = Ok { - value: Str.toUtf8 " binary", - rest: Str.toUtf8 "\r\nSome text here...", - } + input = Str.to_utf8("\r\nContent-Transfer-Encoding: binary\r\nSome text here...") + actual = parse_content_transfer_encoding_f(input) + expected = Ok({ + value: Str.to_utf8(" binary"), + rest: Str.to_utf8("\r\nSome text here..."), + }) actual == expected ## Parses all headers: Content-Disposition, Content-Type and Content-Transfer-Encoding. -parseAllHeaders : List U8 -> Result FormData _ -parseAllHeaders = \bytes -> +parse_all_headers : List U8 -> Result FormData _ +parse_all_headers = \bytes -> - doubleNewlineLength = 4 # \r\n\r\n + double_newline_length = 4 # \r\n\r\n - when parseContentDispositionF bytes is - Err err -> Err (ExpectedContentDisposition bytes err) - Ok { value: disposition, rest: first } -> - when parseContentTypeF first is - Err _ -> - Ok { + when parse_content_disposition_f(bytes) is + Err(err) -> Err(ExpectedContentDisposition(bytes, err)) + Ok({ value: disposition, rest: first }) -> + when parse_content_type_f(first) is + Err(_) -> + Ok({ disposition, type: [], encoding: [], - data: List.dropFirst first doubleNewlineLength, - } + data: List.drop_first(first, double_newline_length), + }) - Ok { value: type, rest: second } -> - when parseContentTransferEncodingF second is - Err _ -> - Ok { + Ok({ value: type, rest: second }) -> + when parse_content_transfer_encoding_f(second) is + Err(_) -> + Ok({ disposition, type, encoding: [], - data: List.dropFirst second doubleNewlineLength, - } + data: List.drop_first(second, double_newline_length), + }) - Ok { value: encoding, rest } -> - Ok { + Ok({ value: encoding, rest }) -> + Ok({ disposition, type, encoding, - data: List.dropFirst rest doubleNewlineLength, - } + data: List.drop_first(rest, double_newline_length), + }) expect header = "\r\nContent-Disposition: form-data; name=\"sometext\"\r\n\r\n" - actual = parseAllHeaders (Str.toUtf8 header) - expected = Ok { - disposition: Str.toUtf8 " form-data; name=\"sometext\"", - type: Str.toUtf8 "", - encoding: Str.toUtf8 "", - data: Str.toUtf8 "", - } + actual = parse_all_headers(Str.to_utf8(header)) + expected = Ok({ + disposition: Str.to_utf8(" form-data; name=\"sometext\""), + type: Str.to_utf8(""), + encoding: Str.to_utf8(""), + data: Str.to_utf8(""), + }) actual == expected expect header = "\r\nContent-Disposition: form-data; name=\"sometext\"\r\nContent-Type: multipart/mixed; boundary=abcde\r\n\r\n" - actual = parseAllHeaders (Str.toUtf8 header) - expected = Ok { - disposition: Str.toUtf8 " form-data; name=\"sometext\"", - type: Str.toUtf8 " multipart/mixed; boundary=abcde", - encoding: Str.toUtf8 "", - data: Str.toUtf8 "", - } + actual = parse_all_headers(Str.to_utf8(header)) + expected = Ok({ + disposition: Str.to_utf8(" form-data; name=\"sometext\""), + type: Str.to_utf8(" multipart/mixed; boundary=abcde"), + encoding: Str.to_utf8(""), + data: Str.to_utf8(""), + }) actual == expected expect header = "\r\nContent-Disposition: form-data; name=\"sometext\"\r\nContent-Type: multipart/mixed; boundary=abcde\r\nContent-Transfer-Encoding: binary\r\n\r\n" - actual = parseAllHeaders (Str.toUtf8 header) - expected = Ok { - disposition: Str.toUtf8 " form-data; name=\"sometext\"", - type: Str.toUtf8 " multipart/mixed; boundary=abcde", - encoding: Str.toUtf8 " binary", - data: Str.toUtf8 "", - } + actual = parse_all_headers(Str.to_utf8(header)) + expected = Ok({ + disposition: Str.to_utf8(" form-data; name=\"sometext\""), + type: Str.to_utf8(" multipart/mixed; boundary=abcde"), + encoding: Str.to_utf8(" binary"), + data: Str.to_utf8(""), + }) actual == expected @@ -210,23 +211,23 @@ parse_form_data : -> Result (List FormData) [ExpectedEnclosedByBoundary] parse_form_data = \{ body, boundary } -> - startMarker = List.join [doubledash, boundary] - endMarker = List.join [newline, doubledash, boundary, doubledash, newline] - boundaryWithPrefix = List.join [newline, doubledash, boundary] + start_marker = List.join([doubledash, boundary]) + end_marker = List.join([newline, doubledash, boundary, doubledash, newline]) + boundary_with_prefix = List.join([newline, doubledash, boundary]) - isEnclosedByBoundary = - List.startsWith body startMarker - && List.endsWith body endMarker + is_enclosed_by_boundary = + List.starts_with(body, start_marker) + && List.ends_with(body, end_marker) - if isEnclosedByBoundary then + if is_enclosed_by_boundary then body - |> List.dropFirst (List.len startMarker) - |> SplitList.split_on_list boundaryWithPrefix - |> List.dropIf \part -> part == doubledash - |> List.keepOks parseAllHeaders + |> List.drop_first(List.len(start_marker)) + |> SplitList.split_on_list(boundary_with_prefix) + |> List.drop_if(\part -> part == doubledash) + |> List.keep_oks(parse_all_headers) |> Ok else - Err ExpectedEnclosedByBoundary + Err(ExpectedEnclosedByBoundary) expect # """ @@ -240,57 +241,57 @@ expect # ------WebKitFormBoundaryQGx5ri4XFwWbaAX4-- # # """ - actual = parse_form_data { + actual = parse_form_data({ body: [45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 70, 71, 54, 83, 65, 109, 121, 106, 112, 49, 108, 118, 102, 57, 80, 55, 13, 10, 67, 111, 110, 116, 101, 110, 116, 45, 68, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 58, 32, 102, 111, 114, 109, 45, 100, 97, 116, 97, 59, 32, 110, 97, 109, 101, 61, 34, 102, 105, 108, 101, 34, 59, 32, 102, 105, 108, 101, 110, 97, 109, 101, 61, 34, 115, 97, 109, 112, 108, 101, 50, 46, 99, 115, 118, 34, 13, 10, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 32, 116, 101, 120, 116, 47, 99, 115, 118, 13, 10, 13, 10, 70, 105, 114, 115, 116, 44, 76, 97, 115, 116, 13, 10, 82, 97, 99, 104, 101, 108, 44, 66, 111, 111, 107, 101, 114, 13, 10, 13, 10, 45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 70, 71, 54, 83, 65, 109, 121, 106, 112, 49, 108, 118, 102, 57, 80, 55, 45, 45, 13, 10], boundary: [45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 70, 71, 54, 83, 65, 109, 121, 106, 112, 49, 108, 118, 102, 57, 80, 55], - } + }) # Result.isErr actual - expected = Ok [ + expected = Ok([ { data: [70, 105, 114, 115, 116, 44, 76, 97, 115, 116, 13, 10, 82, 97, 99, 104, 101, 108, 44, 66, 111, 111, 107, 101, 114, 13, 10], disposition: [32, 102, 111, 114, 109, 45, 100, 97, 116, 97, 59, 32, 110, 97, 109, 101, 61, 34, 102, 105, 108, 101, 34, 59, 32, 102, 105, 108, 101, 110, 97, 109, 101, 61, 34, 115, 97, 109, 112, 108, 101, 50, 46, 99, 115, 118, 34], encoding: [], type: [32, 116, 101, 120, 116, 47, 99, 115, 118], }, - ] + ]) actual == expected expect - input = Str.toUtf8 "--12345\r\nContent-Disposition: form-data; name=\"sometext\"\r\n\r\nsome text sent via post...\r\n--12345--\r\n" - actual = parse_form_data { + input = Str.to_utf8("--12345\r\nContent-Disposition: form-data; name=\"sometext\"\r\n\r\nsome text sent via post...\r\n--12345--\r\n") + actual = parse_form_data({ body: input, - boundary: Str.toUtf8 "12345", - } - expected = Ok [ + boundary: Str.to_utf8("12345"), + }) + expected = Ok([ { - disposition: Str.toUtf8 " form-data; name=\"sometext\"", + disposition: Str.to_utf8(" form-data; name=\"sometext\""), type: [], encoding: [], - data: Str.toUtf8 "some text sent via post...", + data: Str.to_utf8("some text sent via post..."), }, - ] + ]) actual == expected expect - body = Str.toUtf8 "--AaB03x\r\nContent-Disposition: form-data; name=\"submit-name\"\r\n\r\nLarry\r\n--AaB03x\r\nContent-Disposition: form-data; name=\"files\"\r\nContent-Type: multipart/mixed; boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-Disposition: file; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\n... contents of file1.txt ...\r\n--BbC04y\r\nContent-Disposition: file; filename=\"file2.gif\"\r\nContent-Type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\n...contents of file2.gif...\r\n--BbC04y--\r\n--AaB03x--\r\n" - boundary = Str.toUtf8 "AaB03x" - actual = parse_form_data { body, boundary } - expected = Ok [ + body = Str.to_utf8("--AaB03x\r\nContent-Disposition: form-data; name=\"submit-name\"\r\n\r\nLarry\r\n--AaB03x\r\nContent-Disposition: form-data; name=\"files\"\r\nContent-Type: multipart/mixed; boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-Disposition: file; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\n... contents of file1.txt ...\r\n--BbC04y\r\nContent-Disposition: file; filename=\"file2.gif\"\r\nContent-Type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\n...contents of file2.gif...\r\n--BbC04y--\r\n--AaB03x--\r\n") + boundary = Str.to_utf8("AaB03x") + actual = parse_form_data({ body, boundary }) + expected = Ok([ { - disposition: Str.toUtf8 " form-data; name=\"submit-name\"", + disposition: Str.to_utf8(" form-data; name=\"submit-name\""), type: [], encoding: [], - data: Str.toUtf8 "Larry", + data: Str.to_utf8("Larry"), }, { - disposition: Str.toUtf8 " form-data; name=\"files\"", - type: Str.toUtf8 " multipart/mixed; boundary=BbC04y", + disposition: Str.to_utf8(" form-data; name=\"files\""), + type: Str.to_utf8(" multipart/mixed; boundary=BbC04y"), encoding: [], - data: Str.toUtf8 "--BbC04y\r\nContent-Disposition: file; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\n... contents of file1.txt ...\r\n--BbC04y\r\nContent-Disposition: file; filename=\"file2.gif\"\r\nContent-Type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\n...contents of file2.gif...\r\n--BbC04y--", + data: Str.to_utf8("--BbC04y\r\nContent-Disposition: file; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\n... contents of file1.txt ...\r\n--BbC04y\r\nContent-Disposition: file; filename=\"file2.gif\"\r\nContent-Type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\n...contents of file2.gif...\r\n--BbC04y--"), }, - ] + ]) actual == expected @@ -306,75 +307,75 @@ expect parse_form_url_encoded : List U8 -> Result (Dict Str Str) [BadUtf8] parse_form_url_encoded = \bytes -> - chainUtf8 = \bytesList, tryFun -> Str.fromUtf8 bytesList |> mapUtf8Err |> Result.try tryFun + chain_utf8 = \bytes_list, try_fun -> Str.from_utf8(bytes_list) |> map_utf8_err |> Result.try(try_fun) # simplify `BadUtf8 Utf8ByteProblem ...` error - mapUtf8Err = \err -> err |> Result.mapErr \_ -> BadUtf8 + map_utf8_err = \err -> err |> Result.map_err(\_ -> BadUtf8) - help = \bytesRemaining, state, key, chomped, dict -> - tail = List.dropFirst bytesRemaining 1 + help = \bytes_remaining, state, key, chomped, dict -> + tail = List.drop_first(bytes_remaining, 1) - when bytesRemaining is - [] if List.isEmpty chomped -> dict |> Ok + when bytes_remaining is + [] if List.is_empty(chomped) -> dict |> Ok [] -> # chomped last value key - |> chainUtf8 \keyStr -> + |> chain_utf8(\key_str -> chomped - |> chainUtf8 \valueStr -> - Dict.insert dict keyStr valueStr |> Ok + |> chain_utf8(\value_str -> + Dict.insert(dict, key_str, value_str) |> Ok)) - ['=', ..] -> help tail ParsingValue chomped [] dict # put chomped into key + ['=', ..] -> help(tail, ParsingValue, chomped, [], dict) # put chomped into key ['&', ..] -> key - |> chainUtf8 \keyStr -> + |> chain_utf8(\key_str -> chomped - |> chainUtf8 \valueStr -> - help tail ParsingKey [] [] (Dict.insert dict keyStr valueStr) + |> chain_utf8(\value_str -> + help(tail, ParsingKey, [], [], Dict.insert(dict, key_str, value_str)))) - ['%', secondByte, thirdByte, ..] -> - hex = Num.toU8 (hexBytesToU32 [secondByte, thirdByte]) + ['%', second_byte, third_byte, ..] -> + hex = Num.to_u8(hex_bytes_to_u32([second_byte, third_byte])) - help (List.dropFirst tail 2) state key (List.append chomped hex) dict + help(List.drop_first(tail, 2), state, key, List.append(chomped, hex), dict) - [firstByte, ..] -> help tail state key (List.append chomped firstByte) dict + [first_byte, ..] -> help(tail, state, key, List.append(chomped, first_byte), dict) - help bytes ParsingKey [] [] (Dict.empty {}) + help(bytes, ParsingKey, [], [], Dict.empty({})) -expect hexBytesToU32 ['2', '0'] == 32 +expect hex_bytes_to_u32(['2', '0']) == 32 expect - bytes = Str.toUtf8 "todo=foo&status=bar" - parsed = parse_form_url_encoded bytes |> Result.withDefault (Dict.empty {}) + bytes = Str.to_utf8("todo=foo&status=bar") + parsed = parse_form_url_encoded(bytes) |> Result.with_default(Dict.empty({})) - Dict.toList parsed == [("todo", "foo"), ("status", "bar")] + Dict.to_list(parsed) == [("todo", "foo"), ("status", "bar")] expect - Str.toUtf8 "task=asdfs%20adf&status=qwerwe" + Str.to_utf8("task=asdfs%20adf&status=qwerwe") |> parse_form_url_encoded - |> Result.withDefault (Dict.empty {}) - |> Dict.toList - |> Bool.isEq [("task", "asdfs adf"), ("status", "qwerwe")] + |> Result.with_default(Dict.empty({})) + |> Dict.to_list + |> Bool.is_eq([("task", "asdfs adf"), ("status", "qwerwe")]) -hexBytesToU32 : List U8 -> U32 -hexBytesToU32 = \bytes -> +hex_bytes_to_u32 : List U8 -> U32 +hex_bytes_to_u32 = \bytes -> bytes |> List.reverse - |> List.walkWithIndex 0 \accum, byte, i -> accum + (Num.powInt 16 (Num.toU32 i)) * (hexToDec byte) - |> Num.toU32 - -expect hexBytesToU32 ['0', '0', '0', '0'] == 0 -expect hexBytesToU32 ['0', '0', '0', '1'] == 1 -expect hexBytesToU32 ['0', '0', '0', 'F'] == 15 -expect hexBytesToU32 ['0', '0', '1', '0'] == 16 -expect hexBytesToU32 ['0', '0', 'F', 'F'] == 255 -expect hexBytesToU32 ['0', '1', '0', '0'] == 256 -expect hexBytesToU32 ['0', 'F', 'F', 'F'] == 4095 -expect hexBytesToU32 ['1', '0', '0', '0'] == 4096 -expect hexBytesToU32 ['1', '6', 'F', 'F', '1'] == 94193 - -hexToDec : U8 -> U32 -hexToDec = \byte -> + |> List.walk_with_index(0, \accum, byte, i -> accum + (Num.pow_int(16, Num.to_u32(i))) * (hex_to_dec(byte))) + |> Num.to_u32 + +expect hex_bytes_to_u32(['0', '0', '0', '0']) == 0 +expect hex_bytes_to_u32(['0', '0', '0', '1']) == 1 +expect hex_bytes_to_u32(['0', '0', '0', 'F']) == 15 +expect hex_bytes_to_u32(['0', '0', '1', '0']) == 16 +expect hex_bytes_to_u32(['0', '0', 'F', 'F']) == 255 +expect hex_bytes_to_u32(['0', '1', '0', '0']) == 256 +expect hex_bytes_to_u32(['0', 'F', 'F', 'F']) == 4095 +expect hex_bytes_to_u32(['1', '0', '0', '0']) == 4096 +expect hex_bytes_to_u32(['1', '6', 'F', 'F', '1']) == 94193 + +hex_to_dec : U8 -> U32 +hex_to_dec = \byte -> when byte is '0' -> 0 '1' -> 1 @@ -392,10 +393,10 @@ hexToDec = \byte -> 'D' -> 13 'E' -> 14 'F' -> 15 - _ -> crash "Impossible error: the `when` block I'm in should have matched before reaching the catch-all `_`." + _ -> crash("Impossible error: the `when` block I'm in should have matched before reaching the catch-all `_`.") -expect hexToDec '0' == 0 -expect hexToDec 'F' == 15 +expect hex_to_dec('0') == 0 +expect hex_to_dec('F') == 15 ## For HTML forms that include files or large amounts of text. ## @@ -407,19 +408,19 @@ parse_multipart_form_data : } -> Result (List MultipartFormData.FormData) [InvalidMultipartFormData, ExpectedContentTypeHeader, InvalidContentTypeHeader] parse_multipart_form_data = \args -> - decode_multipart_form_data_boundary args.headers - |> Result.try \boundary -> + decode_multipart_form_data_boundary(args.headers) + |> Result.try(\boundary -> { body: args.body, boundary } |> parse_form_data - |> Result.mapErr \_ -> InvalidMultipartFormData + |> Result.map_err(\_ -> InvalidMultipartFormData)) decode_multipart_form_data_boundary : List { name : Str, value : Str } -> Result (List U8) _ decode_multipart_form_data_boundary = \headers -> headers - |> List.keepIf \{ name } -> name == "Content-Type" || name == "content-type" + |> List.keep_if(\{ name } -> name == "Content-Type" || name == "content-type") |> List.first - |> Result.mapErr \ListWasEmpty -> ExpectedContentTypeHeader - |> Result.try \{ value } -> - when Str.splitLast value "=" is - Ok { after } -> Ok (Str.toUtf8 after) - Err NotFound -> Err InvalidContentTypeHeader + |> Result.map_err(\ListWasEmpty -> ExpectedContentTypeHeader) + |> Result.try(\{ value } -> + when Str.split_last(value, "=") is + Ok({ after }) -> Ok(Str.to_utf8(after)) + Err(NotFound) -> Err(InvalidContentTypeHeader)) diff --git a/platform/Path.roc b/platform/Path.roc index d9ff10c..322ea91 100644 --- a/platform/Path.roc +++ b/platform/Path.roc @@ -69,10 +69,10 @@ IOErr : InternalIOErr.IOErr ## > To write unformatted bytes to a file, you can use [Path.write_bytes!] instead. write! : val, Path, fmt => Result {} [FileWriteErr Path IOErr] where val implements Encoding, fmt implements EncoderFormatting write! = \val, path, fmt -> - bytes = Encode.toBytes val fmt + bytes = Encode.to_bytes(val, fmt) # TODO handle encoding errors here, once they exist - write_bytes! bytes path + write_bytes!(bytes, path) ## Writes bytes to a file. ## @@ -86,10 +86,10 @@ write! = \val, path, fmt -> ## > To format data before writing it to a file, you can use [Path.write!] instead. write_bytes! : List U8, Path => Result {} [FileWriteErr Path IOErr] write_bytes! = \bytes, path -> - path_bytes = InternalPath.to_bytes path + path_bytes = InternalPath.to_bytes(path) - Host.file_write_bytes! path_bytes bytes - |> Result.mapErr \err -> FileWriteErr path (InternalIOErr.handle_err err) + Host.file_write_bytes!(path_bytes, bytes) + |> Result.map_err(\err -> FileWriteErr(path, InternalIOErr.handle_err(err))) ## Writes a [Str] to a file, encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8). ## @@ -103,10 +103,10 @@ write_bytes! = \bytes, path -> ## > To write unformatted bytes to a file, you can use [Path.write_bytes!] instead. write_utf8! : Str, Path => Result {} [FileWriteErr Path IOErr] write_utf8! = \str, path -> - path_bytes = InternalPath.to_bytes path + path_bytes = InternalPath.to_bytes(path) - Host.file_write_utf8! path_bytes str - |> Result.mapErr \err -> FileWriteErr path (InternalIOErr.handle_err err) + Host.file_write_utf8!(path_bytes, str) + |> Result.map_err(\err -> FileWriteErr(path, InternalIOErr.handle_err(err))) ## 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 @@ -119,7 +119,7 @@ write_utf8! = \str, path -> ## 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 + FromStr(str) |> InternalPath.wrap ## Not all filesystems use Unicode paths. This function can be used to create a path which @@ -129,7 +129,7 @@ from_str = \str -> ## (e.g. `Path.read_bytes` or `WriteStream.openPath`) will fail. from_bytes : List U8 -> Path from_bytes = \bytes -> - ArbitraryBytes bytes + ArbitraryBytes(bytes) |> InternalPath.wrap ## Unfortunately, operating system paths do not include information about which charset @@ -165,13 +165,13 @@ from_bytes = \bytes -> ## `toStrUsingCharset` instead of [display]. display : Path -> Str display = \path -> - when InternalPath.unwrap path is - FromStr str -> str - FromOperatingSystem bytes | ArbitraryBytes bytes -> - when Str.fromUtf8 bytes is - Ok str -> str + when InternalPath.unwrap(path) is + FromStr(str) -> str + FromOperatingSystem(bytes) | ArbitraryBytes(bytes) -> + when Str.from_utf8(bytes) is + Ok(str) -> str # TODO: this should use the builtin Str.display to display invalid UTF-8 chars in just the right spots, but that does not exist yet! - Err _ -> "�" + Err(_) -> "�" ## Returns true if the path exists on disk and is pointing at a directory. ## Returns `Ok false` if the path exists and it is not a directory. If the path does not exist, @@ -182,8 +182,8 @@ display = \path -> ## > [`File.is_dir`](File#is_dir!) does the same thing, except it takes a [Str] instead of a [Path]. is_dir! : Path => Result Bool [PathErr IOErr] is_dir! = \path -> - res = type!? path - Ok (res == IsDir) + res = type!?(path) + Ok((res == IsDir)) ## Returns true if the path exists on disk and is pointing at a regular file. ## Returns `Ok false` if the path exists and it is not a file. If the path does not exist, @@ -194,8 +194,8 @@ is_dir! = \path -> ## > [`File.is_file`](File#is_file!) does the same thing, except it takes a [Str] instead of a [Path]. is_file! : Path => Result Bool [PathErr IOErr] is_file! = \path -> - res = type!? path - Ok (res == IsFile) + res = type!?(path) + Ok((res == IsFile)) ## Returns true if the path exists on disk and is pointing at a symbolic link. ## Returns `Ok false` if the path exists and it is not a symbolic link. If the path does not exist, @@ -206,23 +206,23 @@ is_file! = \path -> ## > [`File.is_sym_link`](File#is_sym_link!) does the same thing, except it takes a [Str] instead of a [Path]. is_sym_link! : Path => Result Bool [PathErr IOErr] is_sym_link! = \path -> - res = type!? path - Ok (res == IsSymLink) + res = type!?(path) + Ok((res == IsSymLink)) ## Return the type of the path if the path exists on disk. ## ## > [`File.type`](File#type!) does the same thing, except it takes a [Str] instead of a [Path]. type! : Path => Result [IsFile, IsDir, IsSymLink] [PathErr IOErr] type! = \path -> - Host.path_type! (InternalPath.to_bytes path) - |> Result.mapErr \err -> PathErr (InternalIOErr.handle_err err) - |> Result.map \path_type -> + Host.path_type!(InternalPath.to_bytes(path)) + |> Result.map_err(\err -> PathErr(InternalIOErr.handle_err(err))) + |> Result.map(\path_type -> if path_type.is_sym_link then IsSymLink else if path_type.is_dir then IsDir else - IsFile + IsFile) ## If the last component of this path has no `.`, appends `.` followed by the given string. ## Otherwise, replaces everything after the last `.` with the given string. @@ -235,30 +235,30 @@ type! = \path -> ## ``` with_extension : Path, Str -> Path with_extension = \path, extension -> - when InternalPath.unwrap path is - FromOperatingSystem bytes | ArbitraryBytes bytes -> + when InternalPath.unwrap(path) is + FromOperatingSystem(bytes) | ArbitraryBytes(bytes) -> before_dot = - when List.splitLast bytes (Num.toU8 '.') is - Ok { before } -> before - Err NotFound -> bytes + when List.split_last(bytes, Num.to_u8('.')) is + Ok({ before }) -> before + Err(NotFound) -> bytes before_dot - |> List.reserve (Str.countUtf8Bytes extension |> Num.intCast |> Num.addSaturated 1) - |> List.append (Num.toU8 '.') - |> List.concat (Str.toUtf8 extension) + |> List.reserve((Str.count_utf8_bytes(extension) |> Num.int_cast |> Num.add_saturated(1))) + |> List.append(Num.to_u8('.')) + |> List.concat(Str.to_utf8(extension)) |> ArbitraryBytes |> InternalPath.wrap - FromStr str -> + FromStr(str) -> before_dot = - when Str.splitLast str "." is - Ok { before } -> before - Err NotFound -> str + when Str.split_last(str, ".") is + Ok({ before }) -> before + Err(NotFound) -> str before_dot - |> Str.reserve (Str.countUtf8Bytes extension |> Num.addSaturated 1) - |> Str.concat "." - |> Str.concat extension + |> Str.reserve((Str.count_utf8_bytes(extension) |> Num.add_saturated(1))) + |> Str.concat(".") + |> Str.concat(extension) |> FromStr |> InternalPath.wrap @@ -284,8 +284,8 @@ with_extension = \path, extension -> ## > [`File.delete`](File#delete!) does the same thing, except it takes a [Str] instead of a [Path]. delete! : Path => Result {} [FileWriteErr Path IOErr] delete! = \path -> - Host.file_delete! (InternalPath.to_bytes path) - |> Result.mapErr \err -> FileWriteErr path (InternalIOErr.handle_err err) + Host.file_delete!(InternalPath.to_bytes(path)) + |> Result.map_err(\err -> FileWriteErr(path, InternalIOErr.handle_err(err))) ## Reads a [Str] from a file containing [UTF-8](https://en.wikipedia.org/wiki/UTF-8)-encoded text. ## @@ -303,11 +303,11 @@ delete! = \path -> read_utf8! : Path => Result Str [FileReadErr Path IOErr, FileReadUtf8Err Path _] read_utf8! = \path -> bytes = - Host.file_read_bytes! (InternalPath.to_bytes path) - |> Result.mapErr? \read_err -> FileReadErr path (InternalIOErr.handle_err read_err) + Host.file_read_bytes!(InternalPath.to_bytes(path)) + |> Result.map_err?(\read_err -> FileReadErr(path, InternalIOErr.handle_err(read_err))) - Str.fromUtf8 bytes - |> Result.mapErr \err -> FileReadUtf8Err path err + Str.from_utf8(bytes) + |> Result.map_err(\err -> FileReadUtf8Err(path, err)) ## Reads all the bytes in a file. ## @@ -323,17 +323,17 @@ read_utf8! = \path -> ## > [`File.read_bytes`](File#read_bytes!) does the same thing, except it takes a [Str] instead of a [Path]. read_bytes! : Path => Result (List U8) [FileReadErr Path IOErr] read_bytes! = \path -> - Host.file_read_bytes! (InternalPath.to_bytes path) - |> Result.mapErr \err -> FileReadErr path (InternalIOErr.handle_err err) + Host.file_read_bytes!(InternalPath.to_bytes(path)) + |> Result.map_err(\err -> FileReadErr(path, InternalIOErr.handle_err(err))) ## Lists the files and directories inside the directory. ## ## > [`Dir.list`](Dir#list!) does the same thing, except it takes a [Str] instead of a [Path]. list_dir! : Path => Result (List Path) [DirErr IOErr] list_dir! = \path -> - when Host.dir_list! (InternalPath.to_bytes path) is - Ok entries -> Ok (List.map entries InternalPath.from_os_bytes) - Err err -> Err (DirErr (InternalIOErr.handle_err err)) + when Host.dir_list!(InternalPath.to_bytes(path)) is + Ok(entries) -> Ok(List.map(entries, InternalPath.from_os_bytes)) + Err(err) -> Err(DirErr(InternalIOErr.handle_err(err))) ## Deletes a directory if it's empty ## @@ -346,8 +346,8 @@ list_dir! = \path -> ## > [`Dir.delete_empty`](Dir#delete_empty!) does the same thing, except it takes a [Str] instead of a [Path]. delete_empty! : Path => Result {} [DirErr IOErr] delete_empty! = \path -> - Host.dir_delete_empty! (InternalPath.to_bytes path) - |> Result.mapErr \err -> DirErr (InternalIOErr.handle_err err) + Host.dir_delete_empty!(InternalPath.to_bytes(path)) + |> Result.map_err(\err -> DirErr(InternalIOErr.handle_err(err))) ## Recursively deletes a directory as well as all files and directories ## inside it. @@ -361,8 +361,8 @@ delete_empty! = \path -> ## > [`Dir.delete_all`](Dir#delete_all!) does the same thing, except it takes a [Str] instead of a [Path]. delete_all! : Path => Result {} [DirErr IOErr] delete_all! = \path -> - Host.dir_delete_all! (InternalPath.to_bytes path) - |> Result.mapErr \err -> DirErr (InternalIOErr.handle_err err) + Host.dir_delete_all!(InternalPath.to_bytes(path)) + |> Result.map_err(\err -> DirErr(InternalIOErr.handle_err(err))) ## Creates a directory ## @@ -374,8 +374,8 @@ delete_all! = \path -> ## > [`Dir.create`](Dir#create!) does the same thing, except it takes a [Str] instead of a [Path]. create_dir! : Path => Result {} [DirErr IOErr] create_dir! = \path -> - Host.dir_create! (InternalPath.to_bytes path) - |> Result.mapErr \err -> DirErr (InternalIOErr.handle_err err) + Host.dir_create!(InternalPath.to_bytes(path)) + |> Result.map_err(\err -> DirErr(InternalIOErr.handle_err(err))) ## Creates a directory recursively adding any missing parent directories. ## @@ -386,8 +386,8 @@ create_dir! = \path -> ## > [`Dir.create_all`](Dir#create_all!) does the same thing, except it takes a [Str] instead of a [Path]. create_all! : Path => Result {} [DirErr IOErr] create_all! = \path -> - Host.dir_create_all! (InternalPath.to_bytes path) - |> Result.mapErr \err -> DirErr (InternalIOErr.handle_err err) + Host.dir_create_all!(InternalPath.to_bytes(path)) + |> Result.map_err(\err -> DirErr(InternalIOErr.handle_err(err))) ## Creates a new hard link on the filesystem. ## @@ -399,6 +399,6 @@ create_all! = \path -> ## > [File.hard_link!] does the same thing, except it takes a [Str] instead of a [Path]. hard_link! : Path => Result {} [LinkErr IOErr] hard_link! = \path -> - Host.hard_link! (InternalPath.to_bytes path) - |> Result.mapErr InternalIOErr.handle_err - |> Result.mapErr LinkErr + Host.hard_link!(InternalPath.to_bytes(path)) + |> Result.map_err(InternalIOErr.handle_err) + |> Result.map_err(LinkErr) diff --git a/platform/SplitList.roc b/platform/SplitList.roc index 7261409..51d0aca 100644 --- a/platform/SplitList.roc +++ b/platform/SplitList.roc @@ -15,13 +15,13 @@ split_on_list : List a, List a -> List (List a) where a implements Eq split_on_list = \input_list, separator -> # reserve capacity for markers which mark split boundaries - init_markers = List.withCapacity 100 + init_markers = List.with_capacity(100) # find all the start and stop markers - markers = List.walkWithIndex input_list init_markers (walk_help_find_starts input_list separator) + markers = List.walk_with_index(input_list, init_markers, walk_help_find_starts(input_list, separator)) # split the input based on the markers - walk_split_help input_list markers + walk_split_help(input_list, markers) # produces a Stop, followed by a sequence of Start, Stop, Start, Stop, ... walk_help_find_starts = \input_list, separator_list -> @@ -30,12 +30,12 @@ walk_help_find_starts = \input_list, separator_list -> else \all_markers, _, idx -> - len = List.len separator_list + len = List.len(separator_list) - if List.sublist input_list { start: idx, len } == separator_list then + if List.sublist(input_list, { start: idx, len }) == separator_list then all_markers - |> List.append (Stop idx) - |> List.append (Start (idx + len)) + |> List.append(Stop(idx)) + |> List.append(Start((idx + len))) else all_markers @@ -43,8 +43,8 @@ walk_help_find_starts = \input_list, separator_list -> expect input = [] separator = [1, 2, 3] - help = walk_help_find_starts input separator - actual = List.walkWithIndex input [] help + help = walk_help_find_starts(input, separator) + actual = List.walk_with_index(input, [], help) expected = [] actual == expected @@ -52,8 +52,8 @@ expect expect input = [1, 2, 3] separator = [] - help = walk_help_find_starts input separator - actual = List.walkWithIndex input [] help + help = walk_help_find_starts(input, separator) + actual = List.walk_with_index(input, [], help) expected = [] actual == expected @@ -61,27 +61,27 @@ expect expect input = [3, 4, 5, 6, 7, 8] separator = [3, 4, 5] - help = walk_help_find_starts input separator - actual = List.walkWithIndex input [] help - expected = [Stop 0, Start 3] + help = walk_help_find_starts(input, separator) + actual = List.walk_with_index(input, [], help) + expected = [Stop(0), Start(3)] actual == expected # multiple separators in the middle expect input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3, 4, 5, 6, 7, 8, 9, 10] separator = [3, 4, 5] - help = walk_help_find_starts input separator - actual = List.walkWithIndex input [] help - expected = [Stop 2, Start 5, Stop 10, Start 13] + help = walk_help_find_starts(input, separator) + actual = List.walk_with_index(input, [], help) + expected = [Stop(2), Start(5), Stop(10), Start(13)] actual == expected # separator at end expect input = [6, 7, 8, 3, 4, 5] separator = [3, 4, 5] - help = walk_help_find_starts input separator - actual = List.walkWithIndex input [] help - expected = [Stop 3, Start 6] + help = walk_help_find_starts(input, separator) + actual = List.walk_with_index(input, [], help) + expected = [Stop(3), Start(6)] actual == expected walk_split_help : List a, List [Start U64, Stop U64] -> List (List a) where a implements Eq @@ -89,58 +89,58 @@ walk_split_help = \input, markers -> go = \remaining_markers, state -> when remaining_markers is [] -> state - [Stop stop, .. as rest] if stop == 0 -> go rest state - [Stop stop, .. as rest] -> - go rest (List.append state (List.sublist input { start: 0, len: stop })) + [Stop(stop), .. as rest] if stop == 0 -> go(rest, state) + [Stop(stop), .. as rest] -> + go(rest, List.append(state, List.sublist(input, { start: 0, len: stop }))) - [Start start, Stop stop, .. as rest] -> - go rest (List.append state (List.sublist input { start, len: stop - start })) + [Start(start), Stop(stop), .. as rest] -> + go(rest, List.append(state, List.sublist(input, { start, len: stop - start }))) - [Start start] if start >= List.len input -> state - [Start start] -> - List.append state (List.sublist input { start, len: ((List.len input) - start) }) + [Start(start)] if start >= List.len(input) -> state + [Start(start)] -> + List.append(state, List.sublist(input, { start, len: ((List.len(input)) - start) })) - _ -> crash "Unreachable:\n\tThis list should have matched earlier when branches: $(Inspect.toStr remaining_markers)" + _ -> crash("Unreachable:\n\tThis list should have matched earlier when branches: $(Inspect.to_str(remaining_markers))") - go markers [] + go(markers, []) expect - actual = walk_split_help [1, 2, 3, 5, 6, 7, 8, 9, 10] [Stop 2] + actual = walk_split_help([1, 2, 3, 5, 6, 7, 8, 9, 10], [Stop(2)]) expected = [[1, 2]] actual == expected expect input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3, 4, 5, 6, 7, 8, 9, 10] - actual = walk_split_help input [Stop 2, Start 5, Stop 10, Start 13] + actual = walk_split_help(input, [Stop(2), Start(5), Stop(10), Start(13)]) expected = [[1, 2], [6, 7, 8, 9, 10], [6, 7, 8, 9, 10]] actual == expected expect input = [1, 2, 3, 4, 5, 6, 7, 3, 4, 0, 0] - actual = split_on_list input [3, 4] + actual = split_on_list(input, [3, 4]) expected = [[1, 2], [5, 6, 7], [0, 0]] actual == expected expect input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3, 4, 5, 6, 7, 8, 9, 10] - actual = split_on_list input [3, 4, 5] + actual = split_on_list(input, [3, 4, 5]) expected = [[1, 2], [6, 7, 8, 9, 10], [6, 7, 8, 9, 10]] actual == expected expect input = [One, Two, Three, Four, Five, Six, Seven, Eight, One, Two, Nine, Ten, Three, Four, Five, Six, Seven, One, Two, Eight, Nine, Ten] - actual = split_on_list input [One, Two] + actual = split_on_list(input, [One, Two]) expected = [[Three, Four, Five, Six, Seven, Eight], [Nine, Ten, Three, Four, Five, Six, Seven], [Eight, Nine, Ten]] actual == expected expect input = [6, 7, 8, 3, 4, 5] - actual = split_on_list input [3, 4, 5] + actual = split_on_list(input, [3, 4, 5]) expected = [[6, 7, 8]] actual == expected expect input = [3, 4, 5, 6, 7, 8] - actual = split_on_list input [3, 4, 5] + actual = split_on_list(input, [3, 4, 5]) expected = [[6, 7, 8]] actual == expected diff --git a/platform/Sqlite.roc b/platform/Sqlite.roc index dfe044f..67b8e60 100644 --- a/platform/Sqlite.roc +++ b/platform/Sqlite.roc @@ -145,10 +145,10 @@ Stmt := Box {} ## preparing the query each time it is called. This is usually done in `init!` with the prepared `Stmt` stored in the model. ## ## ``` -## prepared_query = try Sqlite.prepare! { +## prepared_query = Sqlite.prepare!({ ## path : "path/to/database.db", ## query : "SELECT * FROM todos;", -## } +## })? ## ## Sqlite.query_many_prepared! { ## stmt: prepared_query, @@ -166,38 +166,38 @@ prepare! : } => Result Stmt [SqliteErr ErrCode Str] prepare! = \{ path, query: q } -> - Host.sqlite_prepare! path q - |> Result.map @Stmt - |> Result.mapErr internal_to_external_error + Host.sqlite_prepare!(path, q) + |> Result.map(@Stmt) + |> Result.map_err(internal_to_external_error) # internal use only bind! : Stmt, List Binding => Result {} [SqliteErr ErrCode Str] -bind! = \@Stmt stmt, bindings -> - Host.sqlite_bind! stmt bindings - |> Result.mapErr internal_to_external_error +bind! = \@Stmt(stmt), bindings -> + Host.sqlite_bind!(stmt, bindings) + |> Result.map_err(internal_to_external_error) # internal use only columns! : Stmt => List Str -columns! = \@Stmt stmt -> - Host.sqlite_columns! stmt +columns! = \@Stmt(stmt) -> + Host.sqlite_columns!(stmt) # internal use only column_value! : Stmt, U64 => Result Value [SqliteErr ErrCode Str] -column_value! = \@Stmt stmt, i -> - Host.sqlite_column_value! stmt i - |> Result.mapErr internal_to_external_error +column_value! = \@Stmt(stmt), i -> + Host.sqlite_column_value!(stmt, i) + |> Result.map_err(internal_to_external_error) # internal use only step! : Stmt => Result [Row, Done] [SqliteErr ErrCode Str] -step! = \@Stmt stmt -> - Host.sqlite_step! stmt - |> Result.mapErr internal_to_external_error +step! = \@Stmt(stmt) -> + Host.sqlite_step!(stmt) + |> Result.map_err(internal_to_external_error) # internal use only reset! : Stmt => Result {} [SqliteErr ErrCode Str] -reset! = \@Stmt stmt -> - Host.sqlite_reset! stmt - |> Result.mapErr internal_to_external_error +reset! = \@Stmt(stmt) -> + Host.sqlite_reset!(stmt) + |> Result.map_err(internal_to_external_error) ## Execute a SQL statement that doesn't return any rows (like INSERT, UPDATE, DELETE). ## @@ -220,8 +220,8 @@ execute! : } => Result {} [SqliteErr ErrCode Str, UnhandledRows] execute! = \{ path, query: q, bindings } -> - stmt = try prepare! { path, query: q } - execute_prepared! { stmt, bindings } + stmt = prepare!({ path, query: q })? + execute_prepared!({ stmt, bindings }) ## Execute a prepared SQL statement that doesn't return any rows. ## @@ -234,30 +234,31 @@ execute_prepared! : } => Result {} [SqliteErr ErrCode Str, UnhandledRows] execute_prepared! = \{ stmt, bindings } -> - try bind! stmt bindings - res = step! stmt - try reset! stmt + bind!(stmt, bindings)? + res = step!(stmt) + reset!(stmt) + when res is - Ok Done -> - Ok {} + Ok(Done) -> + Ok({}) - Ok Row -> - Err UnhandledRows + Ok(Row) -> + Err(UnhandledRows) - Err e -> - Err e + Err(e) -> + Err(e) ## Execute a SQL query and decode exactly one row into a value. ## ## Example: ## ``` ## # count the number of rows in the `users` table -## count = try Sqlite.query! { +## count = Sqlite.query!({ ## path: db_path, ## query: "SELECT COUNT(*) as \"count\" FROM users;", ## bindings: [], -## row: Sqlite.u64 "count", -## } +## row: Sqlite.u64("count"), +## })? ## ``` query! : { @@ -268,8 +269,8 @@ query! : } => Result a (SqlDecodeErr (RowCountErr err)) query! = \{ path, query: q, bindings, row } -> - stmt = try prepare! { path, query: q } - query_prepared! { stmt, bindings, row } + stmt = prepare!({ path, query: q })? + query_prepared!({ stmt, bindings, row }) ## Execute a prepared SQL query and decode exactly one row into a value. ## @@ -283,9 +284,9 @@ query_prepared! : } => Result a (SqlDecodeErr (RowCountErr err)) query_prepared! = \{ stmt, bindings, row: decode } -> - try bind! stmt bindings - res = decode_exactly_one_row! stmt decode - try reset! stmt + bind!(stmt, bindings)? + res = decode_exactly_one_row!(stmt, decode) + reset!(stmt)? res ## Execute a SQL query and decode multiple rows into a list of values. @@ -311,8 +312,8 @@ query_many! : } => Result (List a) (SqlDecodeErr err) query_many! = \{ path, query: q, bindings, rows } -> - stmt = try prepare! { path, query: q } - query_many_prepared! { stmt, bindings, rows } + stmt = prepare!({ path, query: q })? + query_many_prepared!({ stmt, bindings, rows }) ## Execute a prepared SQL query and decode multiple rows into a list of values. ## @@ -326,9 +327,9 @@ query_many_prepared! : } => Result (List a) (SqlDecodeErr err) query_many_prepared! = \{ stmt, bindings, rows: decode } -> - try bind! stmt bindings - res = decode_rows! stmt decode - try reset! stmt + bind!(stmt, bindings)? + res = decode_rows!(stmt, decode) + reset!(stmt)? res SqlDecodeErr err : [FieldNotFound Str, SqliteErr ErrCode Str]err @@ -344,15 +345,15 @@ SqlDecode a err := List Str -> (Stmt => Result a (SqlDecodeErr err)) ## } ## ``` decode_record : SqlDecode a err, SqlDecode b err, (a, b -> c) -> SqlDecode c err -decode_record = \@SqlDecode gen_first, @SqlDecode gen_second, mapper -> - @SqlDecode \cols -> - decode_first! = gen_first cols - decode_second! = gen_second cols +decode_record = \@SqlDecode(gen_first), @SqlDecode(gen_second), mapper -> + @SqlDecode(\cols -> + decode_first! = gen_first(cols) + decode_second! = gen_second(cols) \stmt -> - first = try decode_first! stmt - second = try decode_second! stmt - Ok (mapper first second) + first = decode_first!(stmt)? + second = decode_second!(stmt)? + Ok(mapper(first, second))) ## Transform the output of a decoder by applying a function to the decoded value. ## @@ -361,69 +362,70 @@ decode_record = \@SqlDecode gen_first, @SqlDecode gen_second, mapper -> ## Sqlite.i64 "id" |> Sqlite.map_value Num.toStr ## ``` map_value : SqlDecode a err, (a -> b) -> SqlDecode b err -map_value = \@SqlDecode gen_decode, mapper -> - @SqlDecode \cols -> - decode! = gen_decode cols +map_value = \@SqlDecode(gen_decode), mapper -> + @SqlDecode(\cols -> + decode! = gen_decode(cols) \stmt -> - val = try decode! stmt - Ok (mapper val) + val = decode!(stmt)? + Ok(mapper(val))) RowCountErr err : [NoRowsReturned, TooManyRowsReturned]err # internal use only decode_exactly_one_row! : Stmt, SqlDecode a (RowCountErr err) => Result a (SqlDecodeErr (RowCountErr err)) -decode_exactly_one_row! = \stmt, @SqlDecode gen_decode -> - cols = columns! stmt - decode_row! = gen_decode cols +decode_exactly_one_row! = \stmt, @SqlDecode(gen_decode) -> + cols = columns!(stmt) + decode_row! = gen_decode(cols) - when try step! stmt is + when step!(stmt)? is Row -> - row = try decode_row! stmt - when try step! stmt is + row = decode_row!(stmt)? + when step!(stmt)? is Done -> - Ok row + Ok(row) Row -> - Err TooManyRowsReturned + Err(TooManyRowsReturned) Done -> - Err NoRowsReturned + Err(NoRowsReturned) # internal use only decode_rows! : Stmt, SqlDecode a err => Result (List a) (SqlDecodeErr err) -decode_rows! = \stmt, @SqlDecode gen_decode -> - cols = columns! stmt - decode_row! = gen_decode cols +decode_rows! = \stmt, @SqlDecode(gen_decode) -> + cols = columns!(stmt) + decode_row! = gen_decode(cols) helper! = \out -> - when try step! stmt is + when step!(stmt)? is Done -> - Ok out + Ok(out) Row -> - row = try decode_row! stmt + row = decode_row!(stmt)? - List.append out row + List.append(out, row) |> helper! - helper! [] + helper!([]) # internal use only decoder : (Value -> Result a (SqlDecodeErr err)) -> (Str -> SqlDecode a err) -decoder = \fn -> \name -> - @SqlDecode \cols -> +decoder = \fn -> + \name -> + @SqlDecode(\cols -> - found = List.findFirstIndex cols \x -> x == name - when found is - Ok index -> - \stmt -> - try column_value! stmt index - |> fn + found = List.find_first_index(cols, \x -> x == name) + when found is + Ok(index) -> + \stmt -> + column_value!(stmt, index)? + |> fn - Err NotFound -> - \_ -> - Err (FieldNotFound name) + Err(NotFound) -> + \_ -> + Err(FieldNotFound(name))) ## Decode a [Value] keeping it tagged. This is useful when data could be many possible types. ## @@ -440,18 +442,18 @@ decoder = \fn -> \name -> ## } ## ``` tagged_value : Str -> SqlDecode Value [] -tagged_value = decoder \val -> - Ok val +tagged_value = decoder(\val -> + Ok(val)) to_unexpected_type_err = \val -> type = when val is - Integer _ -> Integer - Real _ -> Real - String _ -> String - Bytes _ -> Bytes + Integer(_) -> Integer + Real(_) -> Real + String(_) -> String + Bytes(_) -> Bytes Null -> Null - Err (UnexpectedType type) + Err(UnexpectedType(type)) UnexpectedTypeErr : [UnexpectedType [Integer, Real, String, Bytes, Null]] @@ -470,33 +472,33 @@ UnexpectedTypeErr : [UnexpectedType [Integer, Real, String, Bytes, Null]] ## } ## ``` str : Str -> SqlDecode Str UnexpectedTypeErr -str = decoder \val -> +str = decoder(\val -> when val is - String s -> Ok s - _ -> to_unexpected_type_err val + String(s) -> Ok(s) + _ -> to_unexpected_type_err(val)) ## Decode a [Value] to a [List U8]. bytes : Str -> SqlDecode (List U8) UnexpectedTypeErr -bytes = decoder \val -> +bytes = decoder(\val -> when val is - Bytes b -> Ok b - _ -> to_unexpected_type_err val + Bytes(b) -> Ok(b) + _ -> to_unexpected_type_err(val)) # internal use only int_decoder : (I64 -> Result a err) -> (Str -> SqlDecode a [FailedToDecodeInteger err]UnexpectedTypeErr) int_decoder = \cast -> - decoder \val -> + decoder(\val -> when val is - Integer i -> cast i |> Result.mapErr FailedToDecodeInteger - _ -> to_unexpected_type_err val + Integer(i) -> cast(i) |> Result.map_err(FailedToDecodeInteger) + _ -> to_unexpected_type_err(val)) # internal use only real_decoder : (F64 -> Result a err) -> (Str -> SqlDecode a [FailedToDecodeReal err]UnexpectedTypeErr) real_decoder = \cast -> - decoder \val -> + decoder(\val -> when val is - Real r -> cast r |> Result.mapErr FailedToDecodeReal - _ -> to_unexpected_type_err val + Real(r) -> cast(r) |> Result.map_err(FailedToDecodeReal) + _ -> to_unexpected_type_err(val)) ## Decode a [Value] to a [I64]. ## @@ -513,43 +515,43 @@ real_decoder = \cast -> ## } ## ``` i64 : Str -> SqlDecode I64 [FailedToDecodeInteger []]UnexpectedTypeErr -i64 = int_decoder Ok +i64 = int_decoder(Ok) ## Decode a [Value] to a [I32]. i32 : Str -> SqlDecode I32 [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -i32 = int_decoder Num.toI32Checked +i32 = int_decoder(Num.to_i32_checked) ## Decode a [Value] to a [I16]. i16 : Str -> SqlDecode I16 [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -i16 = int_decoder Num.toI16Checked +i16 = int_decoder(Num.to_i16_checked) ## Decode a [Value] to a [I8]. i8 : Str -> SqlDecode I8 [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -i8 = int_decoder Num.toI8Checked +i8 = int_decoder(Num.to_i8_checked) ## Decode a [Value] to a [U64]. u64 : Str -> SqlDecode U64 [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -u64 = int_decoder Num.toU64Checked +u64 = int_decoder(Num.to_u64_checked) ## Decode a [Value] to a [U32]. u32 : Str -> SqlDecode U32 [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -u32 = int_decoder Num.toU32Checked +u32 = int_decoder(Num.to_u32_checked) ## Decode a [Value] to a [U16]. u16 : Str -> SqlDecode U16 [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -u16 = int_decoder Num.toU16Checked +u16 = int_decoder(Num.to_u16_checked) ## Decode a [Value] to a [U8]. u8 : Str -> SqlDecode U8 [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -u8 = int_decoder Num.toU8Checked +u8 = int_decoder(Num.to_u8_checked) ## Decode a [Value] to a [F64]. f64 : Str -> SqlDecode F64 [FailedToDecodeReal []]UnexpectedTypeErr -f64 = real_decoder Ok +f64 = real_decoder(Ok) ## Decode a [Value] to a [F32]. f32 : Str -> SqlDecode F32 [FailedToDecodeReal []]UnexpectedTypeErr -f32 = real_decoder (\x -> Num.toF32 x |> Ok) +f32 = real_decoder(\x -> Num.to_f32(x) |> Ok) # TODO: Mising Num.toDec and Num.toDecChecked # dec = realSqlDecoder Ok @@ -562,77 +564,77 @@ Nullable a : [NotNull a, Null] ## Decode a [Value] to a [Nullable Str]. nullable_str : Str -> SqlDecode (Nullable Str) UnexpectedTypeErr -nullable_str = decoder \val -> +nullable_str = decoder(\val -> when val is - String s -> Ok (NotNull s) - Null -> Ok Null - _ -> to_unexpected_type_err val + String(s) -> Ok(NotNull(s)) + Null -> Ok(Null) + _ -> to_unexpected_type_err(val)) ## Decode a [Value] to a [Nullable (List U8)]. nullable_bytes : Str -> SqlDecode (Nullable (List U8)) UnexpectedTypeErr -nullable_bytes = decoder \val -> +nullable_bytes = decoder(\val -> when val is - Bytes b -> Ok (NotNull b) - Null -> Ok Null - _ -> to_unexpected_type_err val + Bytes(b) -> Ok(NotNull(b)) + Null -> Ok(Null) + _ -> to_unexpected_type_err(val)) # internal use only nullable_int_decoder : (I64 -> Result a err) -> (Str -> SqlDecode (Nullable a) [FailedToDecodeInteger err]UnexpectedTypeErr) nullable_int_decoder = \cast -> - decoder \val -> + decoder(\val -> when val is - Integer i -> cast i |> Result.map NotNull |> Result.mapErr FailedToDecodeInteger - Null -> Ok Null - _ -> to_unexpected_type_err val + Integer(i) -> cast(i) |> Result.map(NotNull) |> Result.map_err(FailedToDecodeInteger) + Null -> Ok(Null) + _ -> to_unexpected_type_err(val)) # internal use only nullable_real_decoder : (F64 -> Result a err) -> (Str -> SqlDecode (Nullable a) [FailedToDecodeReal err]UnexpectedTypeErr) nullable_real_decoder = \cast -> - decoder \val -> + decoder(\val -> when val is - Real r -> cast r |> Result.map NotNull |> Result.mapErr FailedToDecodeReal - Null -> Ok Null - _ -> to_unexpected_type_err val + Real(r) -> cast(r) |> Result.map(NotNull) |> Result.map_err(FailedToDecodeReal) + Null -> Ok(Null) + _ -> to_unexpected_type_err(val)) ## Decode a [Value] to a [Nullable I64]. nullable_i64 : Str -> SqlDecode (Nullable I64) [FailedToDecodeInteger []]UnexpectedTypeErr -nullable_i64 = nullable_int_decoder Ok +nullable_i64 = nullable_int_decoder(Ok) ## Decode a [Value] to a [Nullable I32]. nullable_i32 : Str -> SqlDecode (Nullable I32) [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -nullable_i32 = nullable_int_decoder Num.toI32Checked +nullable_i32 = nullable_int_decoder(Num.to_i32_checked) ## Decode a [Value] to a [Nullable I16]. nullable_i16 : Str -> SqlDecode (Nullable I16) [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -nullable_i16 = nullable_int_decoder Num.toI16Checked +nullable_i16 = nullable_int_decoder(Num.to_i16_checked) ## Decode a [Value] to a [Nullable I8]. nullable_i8 : Str -> SqlDecode (Nullable I8) [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -nullable_i8 = nullable_int_decoder Num.toI8Checked +nullable_i8 = nullable_int_decoder(Num.to_i8_checked) ## Decode a [Value] to a [Nullable U64]. nullable_u64 : Str -> SqlDecode (Nullable U64) [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -nullable_u64 = nullable_int_decoder Num.toU64Checked +nullable_u64 = nullable_int_decoder(Num.to_u64_checked) ## Decode a [Value] to a [Nullable U32]. nullable_u32 : Str -> SqlDecode (Nullable U32) [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -nullable_u32 = nullable_int_decoder Num.toU32Checked +nullable_u32 = nullable_int_decoder(Num.to_u32_checked) ## Decode a [Value] to a [Nullable U16]. nullable_u16 : Str -> SqlDecode (Nullable U16) [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -nullable_u16 = nullable_int_decoder Num.toU16Checked +nullable_u16 = nullable_int_decoder(Num.to_u16_checked) ## Decode a [Value] to a [Nullable U8]. nullable_u8 : Str -> SqlDecode (Nullable U8) [FailedToDecodeInteger [OutOfBounds]]UnexpectedTypeErr -nullable_u8 = nullable_int_decoder Num.toU8Checked +nullable_u8 = nullable_int_decoder(Num.to_u8_checked) ## Decode a [Value] to a [Nullable F64]. nullable_f64 : Str -> SqlDecode (Nullable F64) [FailedToDecodeReal []]UnexpectedTypeErr -nullable_f64 = nullable_real_decoder Ok +nullable_f64 = nullable_real_decoder(Ok) ## Decode a [Value] to a [Nullable F32]. nullable_f32 : Str -> SqlDecode (Nullable F32) [FailedToDecodeReal []]UnexpectedTypeErr -nullable_f32 = nullable_real_decoder (\x -> Num.toF32 x |> Ok) +nullable_f32 = nullable_real_decoder(\x -> Num.to_f32(x) |> Ok) # TODO: Mising Num.toDec and Num.toDecChecked # nullable_dec = nullable_real_decoder Ok @@ -640,7 +642,7 @@ nullable_f32 = nullable_real_decoder (\x -> Num.toF32 x |> Ok) # internal use only internal_to_external_error : InternalSqlite.SqliteError -> [SqliteErr ErrCode Str] internal_to_external_error = \{ code, message } -> - SqliteErr (code_from_i64 code) message + SqliteErr(code_from_i64(code), message) # internal use only code_from_i64 : I64 -> ErrCode @@ -706,7 +708,7 @@ code_from_i64 = \code -> else if code == 101 then Done else - Unknown code + Unknown(code) ## Convert a [ErrCode] to a pretty string for display purposes. errcode_to_str : ErrCode -> Str @@ -742,4 +744,4 @@ errcode_to_str = \code -> Warning -> "Warning: Warnings from sqlite3_log()" Row -> "Row: sqlite3_step() has another row ready" Done -> "Done: sqlite3_step() has finished executing" - Unknown c -> "Unknown: error code $(Num.toStr c) not known" + Unknown(c) -> "Unknown: error code $(Num.to_str(c)) not known" diff --git a/platform/Stderr.roc b/platform/Stderr.roc index d5aedf3..90bbc4c 100644 --- a/platform/Stderr.roc +++ b/platform/Stderr.roc @@ -36,14 +36,14 @@ IOErr : [ handle_err : InternalIOErr.IOErrFromHost -> [StderrErr IOErr] handle_err = \{ tag, msg } -> when tag is - NotFound -> StderrErr NotFound - PermissionDenied -> StderrErr PermissionDenied - BrokenPipe -> StderrErr BrokenPipe - AlreadyExists -> StderrErr AlreadyExists - Interrupted -> StderrErr Interrupted - Unsupported -> StderrErr Unsupported - OutOfMemory -> StderrErr OutOfMemory - Other | EndOfFile -> StderrErr (Other msg) + NotFound -> StderrErr(NotFound) + PermissionDenied -> StderrErr(PermissionDenied) + BrokenPipe -> StderrErr(BrokenPipe) + AlreadyExists -> StderrErr(AlreadyExists) + Interrupted -> StderrErr(Interrupted) + Unsupported -> StderrErr(Unsupported) + OutOfMemory -> StderrErr(OutOfMemory) + Other | EndOfFile -> StderrErr(Other(msg)) ## Write the given string to [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)), ## followed by a newline. @@ -51,8 +51,8 @@ handle_err = \{ tag, msg } -> ## > To write to `stderr` without the newline, see [Stderr.write!]. line! : Str => Result {} [StderrErr IOErr] line! = \str -> - Host.stderr_line! str - |> Result.mapErr handle_err + Host.stderr_line!(str) + |> Result.map_err(handle_err) ## Write the given string to [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). ## @@ -62,5 +62,5 @@ line! = \str -> ## > To write to `stderr` with a newline at the end, see [Stderr.line!]. write! : Str => Result {} [StderrErr IOErr] write! = \str -> - Host.stderr_write! str - |> Result.mapErr handle_err + Host.stderr_write!(str) + |> Result.map_err(handle_err) diff --git a/platform/Stdout.roc b/platform/Stdout.roc index 567ce3e..4dcca59 100644 --- a/platform/Stdout.roc +++ b/platform/Stdout.roc @@ -36,14 +36,14 @@ IOErr : [ handle_err : InternalIOErr.IOErrFromHost -> [StdoutErr IOErr] handle_err = \{ tag, msg } -> when tag is - NotFound -> StdoutErr NotFound - PermissionDenied -> StdoutErr PermissionDenied - BrokenPipe -> StdoutErr BrokenPipe - AlreadyExists -> StdoutErr AlreadyExists - Interrupted -> StdoutErr Interrupted - Unsupported -> StdoutErr Unsupported - OutOfMemory -> StdoutErr OutOfMemory - Other | EndOfFile -> StdoutErr (Other msg) + NotFound -> StdoutErr(NotFound) + PermissionDenied -> StdoutErr(PermissionDenied) + BrokenPipe -> StdoutErr(BrokenPipe) + AlreadyExists -> StdoutErr(AlreadyExists) + Interrupted -> StdoutErr(Interrupted) + Unsupported -> StdoutErr(Unsupported) + OutOfMemory -> StdoutErr(OutOfMemory) + Other | EndOfFile -> StdoutErr(Other(msg)) ## Write the given string to [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)), ## followed by a newline. @@ -52,8 +52,8 @@ handle_err = \{ tag, msg } -> ## line! : Str => Result {} [StdoutErr IOErr] line! = \str -> - Host.stdout_line! str - |> Result.mapErr handle_err + Host.stdout_line!(str) + |> Result.map_err(handle_err) ## Write the given string to [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)). ## @@ -63,5 +63,5 @@ line! = \str -> ## > To write to `stdout` with a newline at the end, see [Stdout.line!]. write! : Str => Result {} [StdoutErr IOErr] write! = \str -> - Host.stdout_write! str - |> Result.mapErr handle_err + Host.stdout_write!(str) + |> Result.map_err(handle_err) diff --git a/platform/Tcp.roc b/platform/Tcp.roc index 797f3d8..a37de1e 100644 --- a/platform/Tcp.roc +++ b/platform/Tcp.roc @@ -42,7 +42,7 @@ parse_connect_err = \err -> "ErrorKind::Interrupted" -> Interrupted "ErrorKind::TimedOut" -> TimedOut "ErrorKind::Unsupported" -> Unsupported - other -> Unrecognized other + other -> Unrecognized(other) ## Represents errors that can occur when performing an effect with a [Stream]. StreamErr : [ @@ -66,7 +66,7 @@ parse_stream_err = \err -> "ErrorKind::Interrupted" -> Interrupted "ErrorKind::OutOfMemory" -> OutOfMemory "ErrorKind::BrokenPipe" -> BrokenPipe - other -> Unrecognized other + other -> Unrecognized(other) ## Opens a TCP connection to a remote host. ## @@ -85,9 +85,9 @@ parse_stream_err = \err -> ## connect! : Str, U16 => Result Stream ConnectErr connect! = \host, port -> - Host.tcp_connect! host port - |> Result.map @Stream - |> Result.mapErr parse_connect_err + Host.tcp_connect!(host, port) + |> Result.map(@Stream) + |> Result.map_err(parse_connect_err) ## Read up to a number of bytes from the TCP stream. ## @@ -99,9 +99,9 @@ connect! = \host, port -> ## ## > To read an exact number of bytes or fail, you can use [Tcp.read_exactly!] instead. read_up_to! : Stream, U64 => Result (List U8) [TcpReadErr StreamErr] -read_up_to! = \@Stream stream, bytes_to_read -> - Host.tcp_read_up_to! stream bytes_to_read - |> Result.mapErr \err -> TcpReadErr (parse_stream_err err) +read_up_to! = \@Stream(stream), bytes_to_read -> + Host.tcp_read_up_to!(stream, bytes_to_read) + |> Result.map_err(\err -> TcpReadErr(parse_stream_err(err))) ## Read an exact number of bytes or fail. ## @@ -112,13 +112,13 @@ read_up_to! = \@Stream stream, bytes_to_read -> ## `TcpUnexpectedEOF` is returned if the stream ends before the specfied number of bytes is reached. ## read_exactly! : Stream, U64 => Result (List U8) [TcpReadErr StreamErr, TcpUnexpectedEOF] -read_exactly! = \@Stream stream, bytes_to_read -> - Host.tcp_read_exactly! stream bytes_to_read - |> Result.mapErr \err -> +read_exactly! = \@Stream(stream), bytes_to_read -> + Host.tcp_read_exactly!(stream, bytes_to_read) + |> Result.map_err(\err -> if err == unexpected_eof_error_message then TcpUnexpectedEOF else - TcpReadErr (parse_stream_err err) + TcpReadErr(parse_stream_err(err))) ## Read until a delimiter or EOF is reached. ## @@ -132,9 +132,9 @@ read_exactly! = \@Stream stream, bytes_to_read -> ## > To read until a newline is found, you can use [Tcp.read_line!] which ## conveniently decodes to a [Str]. read_until! : Stream, U8 => Result (List U8) [TcpReadErr StreamErr] -read_until! = \@Stream stream, byte -> - Host.tcp_read_until! stream byte - |> Result.mapErr \err -> TcpReadErr (parse_stream_err err) +read_until! = \@Stream(stream), byte -> + Host.tcp_read_until!(stream, byte) + |> Result.map_err(\err -> TcpReadErr(parse_stream_err(err))) ## Read until a newline or EOF is reached. ## @@ -148,10 +148,10 @@ read_until! = \@Stream stream, byte -> ## read_line! : Stream => Result Str [TcpReadErr StreamErr, TcpReadBadUtf8 _] read_line! = \stream -> - bytes = read_until!? stream '\n' + bytes = read_until!?(stream, '\n') - Str.fromUtf8 bytes - |> Result.mapErr TcpReadBadUtf8 + Str.from_utf8(bytes) + |> Result.map_err(TcpReadBadUtf8) ## Writes bytes to a TCP stream. ## @@ -162,9 +162,9 @@ read_line! = \stream -> ## ## > To write a [Str], you can use [Tcp.write_utf8!] instead. write! : Stream, List U8 => Result {} [TcpWriteErr StreamErr] -write! = \@Stream stream, bytes -> - Host.tcp_write! stream bytes - |> Result.mapErr \err -> TcpWriteErr (parse_stream_err err) +write! = \@Stream(stream), bytes -> + Host.tcp_write!(stream, bytes) + |> Result.map_err(\err -> TcpWriteErr(parse_stream_err(err))) ## Writes a [Str] to a TCP stream, encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8). ## @@ -176,7 +176,7 @@ write! = \@Stream stream, bytes -> ## > To write unformatted bytes, you can use [Tcp.write!] instead. write_utf8! : Stream, Str => Result {} [TcpWriteErr StreamErr] write_utf8! = \stream, str -> - write! stream (Str.toUtf8 str) + write!(stream, Str.to_utf8(str)) ## Convert a [ConnectErr] to a [Str] you can print. ## @@ -196,7 +196,7 @@ connect_err_to_str = \err -> Interrupted -> "Interrupted" TimedOut -> "TimedOut" Unsupported -> "Unsupported" - Unrecognized message -> "Unrecognized Error: $(message)" + Unrecognized(message) -> "Unrecognized Error: $(message)" ## Convert a [StreamErr] to a [Str] you can print. ## @@ -221,4 +221,4 @@ stream_err_to_str = \err -> Interrupted -> "Interrupted" OutOfMemory -> "OutOfMemory" BrokenPipe -> "BrokenPipe" - Unrecognized message -> "Unrecognized Error: $(message)" + Unrecognized(message) -> "Unrecognized Error: $(message)" diff --git a/platform/Url.roc b/platform/Url.roc index 71bc74a..e23e955 100644 --- a/platform/Url.roc +++ b/platform/Url.roc @@ -38,8 +38,8 @@ Url := Str implements [Inspect] ## on a [Str] first, and then pass that string to [Url.from_str]. This function will make use ## of the extra capacity. reserve : Url, U64 -> Url -reserve = \@Url str, cap -> - @Url (Str.reserve str (Num.intCast cap)) +reserve = \@Url(str), cap -> + @Url(Str.reserve(str, Num.int_cast(cap))) ## Create a [Url] without validating or [percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding) ## anything. @@ -63,7 +63,7 @@ reserve = \@Url str, cap -> ## Naturally, passing invalid URLs to functions that need valid ones will tend to result in errors. ## from_str : Str -> Url -from_str = \str -> @Url str +from_str = \str -> @Url(str) ## Return a [Str] representation of this URL. ## ``` @@ -73,7 +73,7 @@ from_str = \str -> @Url str ## |> Url.to_str ## ``` to_str : Url -> Str -to_str = \@Url str -> str +to_str = \@Url(str) -> str ## [Percent-encodes](https://en.wikipedia.org/wiki/Percent-encoding) a ## [path component](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax) @@ -101,80 +101,80 @@ to_str = \@Url str -> str ## |> Url.append "" ## ``` append : Url, Str -> Url -append = \@Url url_str, suffix_unencoded -> - suffix = percent_encode suffix_unencoded +append = \@Url(url_str), suffix_unencoded -> + suffix = percent_encode(suffix_unencoded) - when Str.splitFirst url_str "?" is - Ok { before, after } -> + when Str.split_first(url_str, "?") is + Ok({ before, after }) -> bytes = - Str.countUtf8Bytes before + Str.count_utf8_bytes(before) + 1 # for "/" - + Str.countUtf8Bytes suffix + + Str.count_utf8_bytes(suffix) + 1 # for "?" - + Str.countUtf8Bytes after + + Str.count_utf8_bytes(after) before - |> Str.reserve bytes - |> append_help suffix - |> Str.concat "?" - |> Str.concat after + |> Str.reserve(bytes) + |> append_help(suffix) + |> Str.concat("?") + |> Str.concat(after) |> @Url - Err NotFound -> + Err(NotFound) -> # There wasn't a query, but there might still be a fragment - when Str.splitFirst url_str "#" is - Ok { before, after } -> + when Str.split_first(url_str, "#") is + Ok({ before, after }) -> bytes = - Str.countUtf8Bytes before + Str.count_utf8_bytes(before) + 1 # for "/" - + Str.countUtf8Bytes suffix + + Str.count_utf8_bytes(suffix) + 1 # for "#" - + Str.countUtf8Bytes after + + Str.count_utf8_bytes(after) before - |> Str.reserve bytes - |> append_help suffix - |> Str.concat "#" - |> Str.concat after + |> Str.reserve(bytes) + |> append_help(suffix) + |> Str.concat("#") + |> Str.concat(after) |> @Url - Err NotFound -> + Err(NotFound) -> # No query and no fragment, so just append it - @Url (append_help url_str suffix) + @Url(append_help(url_str, suffix)) ## Internal helper append_help : Str, Str -> Str append_help = \prefix, suffix -> - if Str.endsWith prefix "/" then - if Str.startsWith suffix "/" then + if Str.ends_with(prefix, "/") then + if Str.starts_with(suffix, "/") then # Avoid a double-slash by appending only the part of the suffix after the "/" - when Str.splitFirst suffix "/" is - Ok { after } -> + when Str.split_first(suffix, "/") is + Ok({ after }) -> # TODO `expect before == ""` - Str.concat prefix after + Str.concat(prefix, after) - Err NotFound -> + Err(NotFound) -> # This should never happen, because we already verified # that the suffix startsWith "/" # TODO `expect Bool.false` here with a comment - Str.concat prefix suffix + Str.concat(prefix, suffix) else # prefix ends with "/" but suffix doesn't start with one, so just append. - Str.concat prefix suffix - else if Str.startsWith suffix "/" then + Str.concat(prefix, suffix) + else if Str.starts_with(suffix, "/") then # Suffix starts with "/" but prefix doesn't end with one, so just append them. - Str.concat prefix suffix - else if Str.isEmpty prefix then + Str.concat(prefix, suffix) + else if Str.is_empty(prefix) then # Prefix is empty; return suffix. suffix - else if Str.isEmpty suffix then + else if Str.is_empty(suffix) then # Suffix is empty; return prefix. prefix else # Neither is empty, but neither has a "/", so add one in between. prefix - |> Str.concat "/" - |> Str.concat suffix + |> Str.concat("/") + |> Str.concat(suffix) ## Internal helper. This is intentionally unexposed so that you don't accidentally ## double-encode things. If you really want to percent-encode an arbitrary string, @@ -194,10 +194,10 @@ percent_encode : Str -> Str percent_encode = \input -> # Optimistically assume we won't need any percent encoding, and can have # the same capacity as the input string. If we're wrong, it will get doubled. - initial_output = List.withCapacity (Str.countUtf8Bytes input |> Num.intCast) + initial_output = List.with_capacity((Str.count_utf8_bytes(input) |> Num.int_cast)) answer = - List.walk (Str.toUtf8 input) initial_output \output, byte -> + List.walk(Str.to_utf8(input), initial_output, \output, byte -> # Spec for percent-encoding: https://www.ietf.org/rfc/rfc3986.txt if (byte >= 97 && byte <= 122) # lowercase ASCII @@ -206,7 +206,7 @@ percent_encode = \input -> then # This is the most common case: an unreserved character, # which needs no encoding in a path - List.append output byte + List.append(output, byte) else when byte is 46 # '.' @@ -214,18 +214,18 @@ percent_encode = \input -> | 126 # '~' | 150 -> # '-' # These special characters can all be unescaped in paths - List.append output byte + List.append(output, byte) _ -> # This needs encoding in a path suffix = - Str.toUtf8 percent_encoded - |> List.sublist { len: 3, start: 3 * Num.intCast byte } + Str.to_utf8(percent_encoded) + |> List.sublist({ len: 3, start: 3 * Num.int_cast(byte) }) - List.concat output suffix + List.concat(output, suffix)) - Str.fromUtf8 answer - |> Result.withDefault "" # This should never fail + Str.from_utf8(answer) + |> Result.with_default("") # This should never fail ## Adds a [Str] query parameter to the end of the [Url]. ## @@ -247,35 +247,35 @@ percent_encode = \input -> ## ``` ## append_param : Url, Str, Str -> Url -append_param = \@Url url_str, key, value -> +append_param = \@Url(url_str), key, value -> { without_fragment, after_query } = - when Str.splitLast url_str "#" is - Ok { before, after } -> + when Str.split_last(url_str, "#") is + Ok({ before, after }) -> # The fragment is almost certainly going to be a small string, # so this interpolation should happen on the stack. { without_fragment: before, after_query: "#$(after)" } - Err NotFound -> + Err(NotFound) -> { without_fragment: url_str, after_query: "" } - encoded_key = percent_encode key - encoded_value = percent_encode value + encoded_key = percent_encode(key) + encoded_value = percent_encode(value) bytes = - Str.countUtf8Bytes without_fragment + Str.count_utf8_bytes(without_fragment) + 1 # for "?" or "&" - + Str.countUtf8Bytes encoded_key + + Str.count_utf8_bytes(encoded_key) + 1 # for "=" - + Str.countUtf8Bytes encoded_value - + Str.countUtf8Bytes after_query + + Str.count_utf8_bytes(encoded_value) + + Str.count_utf8_bytes(after_query) without_fragment - |> Str.reserve bytes - |> Str.concat (if has_query (@Url without_fragment) then "&" else "?") - |> Str.concat encoded_key - |> Str.concat "=" - |> Str.concat encoded_value - |> Str.concat after_query + |> Str.reserve(bytes) + |> Str.concat((if has_query(@Url(without_fragment)) then "&" else "?")) + |> Str.concat(encoded_key) + |> Str.concat("=") + |> Str.concat(encoded_value) + |> Str.concat(after_query) |> @Url ## Replaces the URL's [query](https://en.wikipedia.org/wiki/URL#Syntax)—the part @@ -293,36 +293,36 @@ append_param = \@Url url_str, key, value -> ## |> Url.with_query "" ## ``` with_query : Url, Str -> Url -with_query = \@Url url_str, query_str -> +with_query = \@Url(url_str), query_str -> { without_fragment, after_query } = - when Str.splitLast url_str "#" is - Ok { before, after } -> + when Str.split_last(url_str, "#") is + Ok({ before, after }) -> # The fragment is almost certainly going to be a small string, # so this interpolation should happen on the stack. { without_fragment: before, after_query: "#$(after)" } - Err NotFound -> + Err(NotFound) -> { without_fragment: url_str, after_query: "" } before_query = - when Str.splitLast without_fragment "?" is - Ok { before } -> before - Err NotFound -> without_fragment + when Str.split_last(without_fragment, "?") is + Ok({ before }) -> before + Err(NotFound) -> without_fragment - if Str.isEmpty query_str then - @Url (Str.concat before_query after_query) + if Str.is_empty(query_str) then + @Url(Str.concat(before_query, after_query)) else bytes = - Str.countUtf8Bytes before_query + Str.count_utf8_bytes(before_query) + 1 # for "?" - + Str.countUtf8Bytes query_str - + Str.countUtf8Bytes after_query + + Str.count_utf8_bytes(query_str) + + Str.count_utf8_bytes(after_query) before_query - |> Str.reserve bytes - |> Str.concat "?" - |> Str.concat query_str - |> Str.concat after_query + |> Str.reserve(bytes) + |> Str.concat("?") + |> Str.concat(query_str) + |> Str.concat(after_query) |> @Url ## Returns the URL's [query](https://en.wikipedia.org/wiki/URL#Syntax)—the part after @@ -341,15 +341,15 @@ with_query = \@Url url_str, query_str -> ## ``` ## query : Url -> Str -query = \@Url url_str -> +query = \@Url(url_str) -> without_fragment = - when Str.splitLast url_str "#" is - Ok { before } -> before - Err NotFound -> url_str + when Str.split_last(url_str, "#") is + Ok({ before }) -> before + Err(NotFound) -> url_str - when Str.splitLast without_fragment "?" is - Ok { after } -> after - Err NotFound -> "" + when Str.split_last(without_fragment, "?") is + Ok({ after }) -> after + Err(NotFound) -> "" ## Returns [Bool.true] if the URL has a `?` in it. ## @@ -364,8 +364,8 @@ query = \@Url url_str -> ## ``` ## has_query : Url -> Bool -has_query = \@Url url_str -> - Str.contains url_str "?" +has_query = \@Url(url_str) -> + Str.contains(url_str, "?") ## Returns the URL's [fragment](https://en.wikipedia.org/wiki/URL#Syntax)—the part after ## the `#`, if it has one. @@ -383,10 +383,10 @@ has_query = \@Url url_str -> ## ``` ## fragment : Url -> Str -fragment = \@Url url_str -> - when Str.splitLast url_str "#" is - Ok { after } -> after - Err NotFound -> "" +fragment = \@Url(url_str) -> + when Str.split_last(url_str, "#") is + Ok({ after }) -> after + Err(NotFound) -> "" ## Replaces the URL's [fragment](https://en.wikipedia.org/wiki/URL#Syntax). ## @@ -407,23 +407,23 @@ fragment = \@Url url_str -> ## ``` ## with_fragment : Url, Str -> Url -with_fragment = \@Url url_str, fragment_str -> - when Str.splitLast url_str "#" is - Ok { before } -> - if Str.isEmpty fragment_str then +with_fragment = \@Url(url_str), fragment_str -> + when Str.split_last(url_str, "#") is + Ok({ before }) -> + if Str.is_empty(fragment_str) then # If the given fragment is empty, remove the URL's fragment - @Url before + @Url(before) else # Replace the URL's old fragment with this one, discarding `after` - @Url "$(before)#$(fragment_str)" + @Url("$(before)#$(fragment_str)") - Err NotFound -> - if Str.isEmpty fragment_str then + Err(NotFound) -> + if Str.is_empty(fragment_str) then # If the given fragment is empty, leave the URL as having no fragment - @Url url_str + @Url(url_str) else # The URL didn't have a fragment, so give it this one - @Url "$(url_str)#$(fragment_str)" + @Url("$(url_str)#$(fragment_str)") ## Returns [Bool.true] if the URL has a `#` in it. ## @@ -438,8 +438,8 @@ with_fragment = \@Url url_str, fragment_str -> ## ``` ## has_fragment : Url -> Bool -has_fragment = \@Url url_str -> - Str.contains url_str "#" +has_fragment = \@Url(url_str) -> + Str.contains(url_str, "#") # Adapted from the percent-encoding crate, © The rust-url developers, Apache2-licensed # @@ -449,12 +449,12 @@ percent_encoded = "%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13% query_params : Url -> Dict Str Str query_params = \url -> - query url - |> Str.splitOn "&" - |> List.walk (Dict.empty {}) \dict, pair -> - when Str.splitFirst pair "=" is - Ok { before, after } -> Dict.insert dict before after - Err NotFound -> Dict.insert dict pair "" + query(url) + |> Str.split_on("&") + |> List.walk(Dict.empty({}), \dict, pair -> + when Str.split_first(pair, "=") is + Ok({ before, after }) -> Dict.insert(dict, before, after) + Err(NotFound) -> Dict.insert(dict, pair, "")) ## Returns the URL's [path](https://en.wikipedia.org/wiki/URL#Syntax)—the part after ## the scheme and authority (e.g. `https://`) but before any `?` or `#` it might have. @@ -473,23 +473,23 @@ query_params = \url -> ## |> Url.path ## ``` path : Url -> Str -path = \@Url urlStr -> - withoutAuthority = - when Str.splitFirst urlStr ":" is - Ok { after } -> - when Str.splitFirst after "//" is +path = \@Url(url_str) -> + without_authority = + when Str.split_first(url_str, ":") is + Ok({ after }) -> + when Str.split_first(after, "//") is # Only drop the `//` if it's right after the `://` like in `https://` # (so, `before` is empty) - otherwise, the `//` is part of the path! - Ok { before, after: afterSlashes } if Str.isEmpty before -> afterSlashes + Ok({ before, after: after_slashes }) if Str.is_empty(before) -> after_slashes _ -> after # There's no `//` and also no `:` so this must be a path-only URL, e.g. "/foo?bar=baz#blah" - Err NotFound -> urlStr + Err(NotFound) -> url_str # Drop the query and/or fragment - when Str.splitLast withoutAuthority "?" is - Ok { before } -> before - Err NotFound -> - when Str.splitLast withoutAuthority "#" is - Ok { before } -> before - Err NotFound -> withoutAuthority + when Str.split_last(without_authority, "?") is + Ok({ before }) -> before + Err(NotFound) -> + when Str.split_last(without_authority, "#") is + Ok({ before }) -> before + Err(NotFound) -> without_authority diff --git a/platform/Utc.roc b/platform/Utc.roc index 9415b40..7703d19 100644 --- a/platform/Utc.roc +++ b/platform/Utc.roc @@ -19,24 +19,24 @@ Utc := I128 implements [Inspect] ## Duration since UNIX EPOCH now! : {} => Utc now! = \{} -> - @Utc (Num.toI128 (Host.posix_time! {})) + @Utc(Num.to_i128(Host.posix_time!({}))) # Constant number of nanoseconds in a millisecond nanos_per_milli = 1_000_000 ## Convert Utc timestamp to milliseconds to_millis_since_epoch : Utc -> I128 -to_millis_since_epoch = \@Utc nanos -> +to_millis_since_epoch = \@Utc(nanos) -> nanos // nanos_per_milli ## Convert milliseconds to Utc timestamp from_millis_since_epoch : I128 -> Utc from_millis_since_epoch = \millis -> - @Utc (millis * nanos_per_milli) + @Utc((millis * nanos_per_milli)) ## Convert Utc timestamp to nanoseconds to_nanos_since_epoch : Utc -> I128 -to_nanos_since_epoch = \@Utc nanos -> +to_nanos_since_epoch = \@Utc(nanos) -> nanos ## Convert nanoseconds to Utc timestamp @@ -46,34 +46,34 @@ from_nanos_since_epoch = @Utc ## Calculate milliseconds between two Utc timestamps delta_as_millis : Utc, Utc -> U128 delta_as_millis = \utc_a, utc_b -> - (delta_as_nanos utc_a utc_b) // nanos_per_milli + (delta_as_nanos(utc_a, utc_b)) // nanos_per_milli ## Calculate nanoseconds between two Utc timestamps delta_as_nanos : Utc, Utc -> U128 -delta_as_nanos = \@Utc nanos_a, @Utc nanos_b -> +delta_as_nanos = \@Utc(nanos_a), @Utc(nanos_b) -> # bitwiseXor for best performance - nanos_a_shifted = Num.bitwiseXor (Num.toU128 nanos_a) (Num.shiftLeftBy 1 127) - nanos_b_shifted = Num.bitwiseXor (Num.toU128 nanos_b) (Num.shiftLeftBy 1 127) + nanos_a_shifted = Num.bitwise_xor(Num.to_u128(nanos_a), Num.shift_left_by(1, 127)) + nanos_b_shifted = Num.bitwise_xor(Num.to_u128(nanos_b), Num.shift_left_by(1, 127)) - Num.absDiff nanos_a_shifted nanos_b_shifted + Num.abs_diff(nanos_a_shifted, nanos_b_shifted) ## Convert Utc timestamp to ISO 8601 string ## Example: 2023-11-14T23:39:39Z to_iso_8601 : Utc -> Str -to_iso_8601 = \@Utc nanos -> +to_iso_8601 = \@Utc(nanos) -> nanos - |> Num.divTrunc nanos_per_milli + |> Num.div_trunc(nanos_per_milli) |> InternalDateTime.epoch_millis_to_datetime |> InternalDateTime.to_iso_8601 # TESTS -expect delta_as_nanos (from_nanos_since_epoch 0) (from_nanos_since_epoch 0) == 0 -expect delta_as_nanos (from_nanos_since_epoch 1) (from_nanos_since_epoch 2) == 1 -expect delta_as_nanos (from_nanos_since_epoch -1) (from_nanos_since_epoch 1) == 2 -expect delta_as_nanos (from_nanos_since_epoch Num.minI128) (from_nanos_since_epoch Num.maxI128) == Num.maxU128 +expect delta_as_nanos(from_nanos_since_epoch(0), from_nanos_since_epoch(0)) == 0 +expect delta_as_nanos(from_nanos_since_epoch(1), from_nanos_since_epoch(2)) == 1 +expect delta_as_nanos(from_nanos_since_epoch(-1), from_nanos_since_epoch(1)) == 2 +expect delta_as_nanos(from_nanos_since_epoch(Num.min_i128), from_nanos_since_epoch(Num.max_i128)) == Num.max_u128 -expect delta_as_millis (from_millis_since_epoch 0) (from_millis_since_epoch 0) == 0 -expect delta_as_millis (from_nanos_since_epoch 1) (from_nanos_since_epoch 2) == 0 -expect delta_as_millis (from_millis_since_epoch 1) (from_millis_since_epoch 2) == 1 -expect delta_as_millis (from_millis_since_epoch -1) (from_millis_since_epoch 1) == 2 -expect delta_as_millis (from_nanos_since_epoch Num.minI128) (from_nanos_since_epoch Num.maxI128) == Num.maxU128 // nanos_per_milli +expect delta_as_millis(from_millis_since_epoch(0), from_millis_since_epoch(0)) == 0 +expect delta_as_millis(from_nanos_since_epoch(1), from_nanos_since_epoch(2)) == 0 +expect delta_as_millis(from_millis_since_epoch(1), from_millis_since_epoch(2)) == 1 +expect delta_as_millis(from_millis_since_epoch(-1), from_millis_since_epoch(1)) == 2 +expect delta_as_millis(from_nanos_since_epoch(Num.min_i128), from_nanos_since_epoch(Num.max_i128)) == Num.max_u128 // nanos_per_milli diff --git a/platform/libapp.roc b/platform/libapp.roc index 6c42358..621811d 100644 --- a/platform/libapp.roc +++ b/platform/libapp.roc @@ -8,11 +8,12 @@ Model : {} # That file is an executable that runs a webserver that returns "I'm a stub...". init! : {} => Result Model [] -init! = \_ -> Ok {} +init! = \_ -> Ok({}) respond! : _, _ => Result _ [] -respond! = \_, _ -> Ok { +respond! = \_, _ -> + Ok({ status: 200, headers: [], - body: Str.toUtf8 "I'm a stub, I should be replaced by the user's Roc app.", - } + body: Str.to_utf8("I'm a stub, I should be replaced by the user's Roc app."), + }) diff --git a/platform/main.roc b/platform/main.roc index d6f513f..9c35d14 100644 --- a/platform/main.roc +++ b/platform/main.roc @@ -29,33 +29,34 @@ import InternalHttp init_for_host! : I32 => Result (Box Model) I32 init_for_host! = \_ -> - when init! {} is - Ok model -> Ok (Box.box model) - Err (Exit code msg) -> - if Str.isEmpty msg then - Err code + when init!({}) is + Ok(model) -> Ok(Box.box(model)) + Err(Exit(code, msg)) -> + if Str.is_empty(msg) then + Err(code) else - _ = Stderr.line! msg - Err code + _ = Stderr.line!(msg) + Err(code) - Err err -> - _ = Stderr.line! + Err(err) -> + _ = Stderr.line!( """ Program exited with error: - $(Inspect.toStr err) + $(Inspect.to_str(err)) Tip: If you do not want to exit on this error, use `Result.mapErr` to handle the error. Docs for `Result.mapErr`: - """ - Err 1 + """, + ) + Err(1) respond_for_host! : InternalHttp.RequestToAndFromHost, Box Model => InternalHttp.ResponseToAndFromHost respond_for_host! = \request, boxed_model -> - when respond! (InternalHttp.from_host_request request) (Box.unbox boxed_model) is - Ok response -> InternalHttp.to_host_response response - Err (ServerErr msg) -> + when respond!(InternalHttp.from_host_request(request), Box.unbox(boxed_model)) is + Ok(response) -> InternalHttp.to_host_response(response) + Err(ServerErr(msg)) -> # dicard the err here if stderr fails - _ = Stderr.line! msg + _ = Stderr.line!(msg) # returns a http server error response { @@ -64,16 +65,17 @@ respond_for_host! = \request, boxed_model -> body: [], } - Err err -> + Err(err) -> # dicard the err here if stderr fails - _ = Stderr.line! + _ = Stderr.line!( """ Server error: - $(Inspect.toStr err) + $(Inspect.to_str(err)) Tip: If you do not want to see this error, use `Result.mapErr` to handle the error. Docs for `Result.mapErr`: - """ + """, + ) # returns a http server error response {