mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 19:58:18 +00:00
Merge pull request #4098 from roc-lang/env-decode
Decoding environment variables
This commit is contained in:
commit
8b80eb1728
8 changed files with 257 additions and 46 deletions
|
@ -87,6 +87,7 @@ mod cli_run {
|
|||
executable_filename: &'a str,
|
||||
stdin: &'a [&'a str],
|
||||
arguments: &'a [Arg<'a>],
|
||||
env: &'a [(&'a str, &'a str)],
|
||||
expected_ending: &'a str,
|
||||
use_valgrind: bool,
|
||||
}
|
||||
|
@ -136,12 +137,14 @@ mod cli_run {
|
|||
compile_out
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_output_with_stdin(
|
||||
file: &Path,
|
||||
stdin: &[&str],
|
||||
executable_filename: &str,
|
||||
flags: &[&str],
|
||||
app_args: &[String],
|
||||
extra_env: &[(&str, &str)],
|
||||
expected_ending: &str,
|
||||
use_valgrind: bool,
|
||||
) {
|
||||
|
@ -214,16 +217,31 @@ mod cli_run {
|
|||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
stdin.iter().copied(),
|
||||
app_args,
|
||||
extra_env.iter().copied(),
|
||||
)
|
||||
}
|
||||
}
|
||||
CliMode::Roc => run_roc_on(file, flags.clone(), stdin, app_args),
|
||||
CliMode::RocRun => run_roc_on(
|
||||
file,
|
||||
iter::once(CMD_RUN).chain(flags.clone()),
|
||||
stdin,
|
||||
app_args,
|
||||
),
|
||||
CliMode::Roc => {
|
||||
if !extra_env.is_empty() {
|
||||
// TODO: environment is not currently forwarded by Roc to the target
|
||||
// binary, so this would fail!
|
||||
continue;
|
||||
}
|
||||
run_roc_on(file, flags.clone(), stdin, app_args)
|
||||
}
|
||||
CliMode::RocRun => {
|
||||
if !extra_env.is_empty() {
|
||||
// TODO: environment is not currently forwarded by Roc to the target
|
||||
// binary, so this would fail!
|
||||
continue;
|
||||
}
|
||||
run_roc_on(
|
||||
file,
|
||||
iter::once(CMD_RUN).chain(flags.clone()),
|
||||
stdin,
|
||||
app_args,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
|
@ -361,6 +379,7 @@ mod cli_run {
|
|||
example.executable_filename,
|
||||
&custom_flags,
|
||||
&app_args,
|
||||
example.env,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
);
|
||||
|
@ -375,6 +394,7 @@ mod cli_run {
|
|||
example.executable_filename,
|
||||
&custom_flags,
|
||||
&app_args,
|
||||
example.env,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
);
|
||||
|
@ -388,6 +408,7 @@ mod cli_run {
|
|||
example.executable_filename,
|
||||
&[LINKER_FLAG, "legacy"],
|
||||
&app_args,
|
||||
example.env,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
);
|
||||
|
@ -425,6 +446,7 @@ mod cli_run {
|
|||
executable_filename: "helloWorld",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"Hello, World!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -433,6 +455,7 @@ mod cli_run {
|
|||
executable_filename: "rocLovesPlatforms",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"Which platform am I running on now?\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -452,6 +475,7 @@ mod cli_run {
|
|||
executable_filename: "rocLovesRust",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"Roc <3 Rust!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -460,6 +484,7 @@ mod cli_run {
|
|||
executable_filename: "rocLovesSwift",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"Roc <3 Swift!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -468,6 +493,7 @@ mod cli_run {
|
|||
executable_filename: "rocLovesWebAssembly",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"Roc <3 Web Assembly!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -476,6 +502,7 @@ mod cli_run {
|
|||
executable_filename: "rocLovesZig",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"Roc <3 Zig!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -484,6 +511,7 @@ mod cli_run {
|
|||
executable_filename: "libhello",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -492,6 +520,7 @@ mod cli_run {
|
|||
executable_filename: "fibonacci",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending:"55\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -500,6 +529,7 @@ mod cli_run {
|
|||
executable_filename: "hello-gui",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -508,6 +538,7 @@ mod cli_run {
|
|||
executable_filename: "breakout",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -516,6 +547,7 @@ mod cli_run {
|
|||
executable_filename: "quicksort",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -532,14 +564,28 @@ mod cli_run {
|
|||
executable_filename: "args",
|
||||
stdin: &[],
|
||||
arguments: &[Arg::PlainText("log"), Arg::PlainText("-b"), Arg::PlainText("3"), Arg::PlainText("--num"), Arg::PlainText("81")],
|
||||
env: &[],
|
||||
expected_ending: "4\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
env:"interactive" => Example {
|
||||
filename: "env.roc",
|
||||
executable_filename: "env",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[("EDITOR", "roc-editor"), ("SHLVL", "3"), ("LETTERS", "a,c,e,j")],
|
||||
expected_ending: (
|
||||
"Your favorite editor is roc-editor!\n\
|
||||
Your current shell level is 3!\n\
|
||||
Your favorite letters are: a c e j\n"),
|
||||
use_valgrind: false,
|
||||
},
|
||||
effects:"interactive" => Example {
|
||||
filename: "effects.roc",
|
||||
executable_filename: "effects",
|
||||
stdin: &["hi there!"],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "hi there!\nIt is known\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -556,6 +602,7 @@ mod cli_run {
|
|||
executable_filename: "form",
|
||||
stdin: &["Giovanni\n", "Giorgio\n"],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "Hi, Giovanni Giorgio! 👋\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -564,6 +611,7 @@ mod cli_run {
|
|||
executable_filename: "tui",
|
||||
stdin: &["foo\n"], // NOTE: adding more lines leads to memory leaks
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "Hello Worldfoo!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -589,6 +637,7 @@ mod cli_run {
|
|||
executable_filename: "false",
|
||||
stdin: &[],
|
||||
arguments: &[Arg::ExamplePath("examples/hello.false")],
|
||||
env: &[],
|
||||
expected_ending:"Hello, World!\n",
|
||||
use_valgrind: false,
|
||||
}
|
||||
|
@ -598,6 +647,7 @@ mod cli_run {
|
|||
executable_filename: "swiftui",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -607,6 +657,7 @@ mod cli_run {
|
|||
executable_filename: "static-site",
|
||||
stdin: &[],
|
||||
arguments: &[Arg::ExamplePath("input"), Arg::ExamplePath("output")],
|
||||
env: &[],
|
||||
expected_ending: "Processed 3 files with 3 successes and 0 errors\n",
|
||||
use_valgrind: false,
|
||||
}
|
||||
|
@ -655,6 +706,7 @@ mod cli_run {
|
|||
benchmark.executable_filename,
|
||||
&[],
|
||||
&app_args,
|
||||
benchmark.env,
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
|
@ -673,6 +725,7 @@ mod cli_run {
|
|||
benchmark.executable_filename,
|
||||
&[PREBUILT_PLATFORM],
|
||||
&app_args,
|
||||
benchmark.env,
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
|
@ -684,6 +737,7 @@ mod cli_run {
|
|||
benchmark.executable_filename,
|
||||
&[PREBUILT_PLATFORM, OPTIMIZE_FLAG],
|
||||
&app_args,
|
||||
benchmark.env,
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
|
@ -797,6 +851,7 @@ mod cli_run {
|
|||
executable_filename: "nqueens",
|
||||
stdin: &["6"],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "4\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -805,6 +860,7 @@ mod cli_run {
|
|||
executable_filename: "cfold",
|
||||
stdin: &["3"],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "11 & 11\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -813,6 +869,7 @@ mod cli_run {
|
|||
executable_filename: "deriv",
|
||||
stdin: &["2"],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "1 count: 6\n2 count: 22\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -821,6 +878,7 @@ mod cli_run {
|
|||
executable_filename: "rbtree-ck",
|
||||
stdin: &["100"],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "10\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -829,6 +887,7 @@ mod cli_run {
|
|||
executable_filename: "rbtree-insert",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "Node Black 0 {} Empty Empty\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -837,6 +896,7 @@ mod cli_run {
|
|||
// executable_filename: "rbtree-del",
|
||||
// stdin: &["420"],
|
||||
// arguments: &[],
|
||||
// env: &[],
|
||||
// expected_ending: "30\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
|
@ -845,6 +905,7 @@ mod cli_run {
|
|||
executable_filename: "test-astar",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "True\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -853,6 +914,7 @@ mod cli_run {
|
|||
executable_filename: "test-base64",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "encoded: SGVsbG8gV29ybGQ=\ndecoded: Hello World\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -861,6 +923,7 @@ mod cli_run {
|
|||
executable_filename: "closure",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
|
@ -869,6 +932,7 @@ mod cli_run {
|
|||
executable_filename: "issue2279",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "Hello, world!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -877,6 +941,7 @@ mod cli_run {
|
|||
executable_filename: "quicksortapp",
|
||||
stdin: &[],
|
||||
arguments: &[],
|
||||
env: &[],
|
||||
expected_ending: "todo put the correct quicksort answer here",
|
||||
use_valgrind: true,
|
||||
},
|
||||
|
@ -957,6 +1022,7 @@ mod cli_run {
|
|||
"multi-dep-str",
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
);
|
||||
|
@ -971,6 +1037,7 @@ mod cli_run {
|
|||
"multi-dep-str",
|
||||
&[OPTIMIZE_FLAG],
|
||||
&[],
|
||||
&[],
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
);
|
||||
|
@ -985,6 +1052,7 @@ mod cli_run {
|
|||
"multi-dep-thunk",
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
);
|
||||
|
@ -999,6 +1067,7 @@ mod cli_run {
|
|||
"multi-dep-thunk",
|
||||
&[OPTIMIZE_FLAG],
|
||||
&[],
|
||||
&[],
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
);
|
||||
|
|
|
@ -48,12 +48,12 @@ fn check_cmd_output(
|
|||
let out = if cmd_str.contains("cfold") {
|
||||
let child = thread::Builder::new()
|
||||
.stack_size(CFOLD_STACK_SIZE)
|
||||
.spawn(move || run_cmd(&cmd_str, [stdin_str], &[]))
|
||||
.spawn(move || run_cmd(&cmd_str, [stdin_str], &[], []))
|
||||
.unwrap();
|
||||
|
||||
child.join().unwrap()
|
||||
} else {
|
||||
run_cmd(&cmd_str, [stdin_str], &[])
|
||||
run_cmd(&cmd_str, [stdin_str], &[], [])
|
||||
};
|
||||
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
|
@ -96,13 +96,14 @@ fn bench_cmd<T: Measurement>(
|
|||
|
||||
if let Some(bench_group) = bench_group_opt {
|
||||
bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| {
|
||||
b.iter(|| run_cmd(black_box(&cmd_str), black_box([stdin_str]), &[]))
|
||||
b.iter(|| run_cmd(black_box(&cmd_str), black_box([stdin_str]), &[], []))
|
||||
});
|
||||
} else {
|
||||
run_cmd(
|
||||
black_box(file.with_file_name(executable_filename).to_str().unwrap()),
|
||||
black_box([stdin_str]),
|
||||
&[],
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,10 +161,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
|
||||
pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>, E: IntoIterator<Item = (&'a str, &'a str)>>(
|
||||
cmd_name: &str,
|
||||
stdin_vals: I,
|
||||
args: &[String],
|
||||
env: E,
|
||||
) -> Out {
|
||||
let mut cmd = Command::new(cmd_name);
|
||||
|
||||
|
@ -172,6 +173,10 @@ pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
|
|||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
for (env, val) in env.into_iter() {
|
||||
cmd.env(env, val);
|
||||
}
|
||||
|
||||
let mut child = cmd
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
|
|
|
@ -3732,14 +3732,21 @@ fn send_header<'a>(
|
|||
// Also build a list of imported_values_to_expose (like `bar` above.)
|
||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||
let cloned_module_name = qualified_module_name.module.clone();
|
||||
let pq_module_name = match qualified_module_name.opt_package {
|
||||
None => match opt_shorthand {
|
||||
Some(shorthand) => {
|
||||
PQModuleName::Qualified(shorthand, qualified_module_name.module)
|
||||
}
|
||||
None => PQModuleName::Unqualified(qualified_module_name.module),
|
||||
},
|
||||
Some(package) => PQModuleName::Qualified(package, cloned_module_name),
|
||||
let pq_module_name = if qualified_module_name.is_builtin() {
|
||||
// If this is a builtin, it must be unqualified, and we should *never* prefix it
|
||||
// with the package shorthand! The user intended to import the module as-is here.
|
||||
debug_assert!(qualified_module_name.opt_package.is_none());
|
||||
PQModuleName::Unqualified(qualified_module_name.module)
|
||||
} else {
|
||||
match qualified_module_name.opt_package {
|
||||
None => match opt_shorthand {
|
||||
Some(shorthand) => {
|
||||
PQModuleName::Qualified(shorthand, qualified_module_name.module)
|
||||
}
|
||||
None => PQModuleName::Unqualified(qualified_module_name.module),
|
||||
},
|
||||
Some(package) => PQModuleName::Qualified(package, cloned_module_name),
|
||||
}
|
||||
};
|
||||
|
||||
let module_id = module_ids.get_or_insert(&pq_module_name);
|
||||
|
|
1
examples/interactive/.gitignore
vendored
1
examples/interactive/.gitignore
vendored
|
@ -6,3 +6,4 @@ form
|
|||
tui
|
||||
http-get
|
||||
file-io
|
||||
env
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
interface Env
|
||||
exposes [cwd, dict, var]
|
||||
imports [Task.{ Task }, Path.{ Path }, InternalPath, Effect, InternalTask]
|
||||
exposes [cwd, dict, var, decode] imports [Task.{ Task }, Path.{ Path }, InternalPath, Effect, InternalTask, EnvDecoding]
|
||||
|
||||
## Reads the [current working directory](https://en.wikipedia.org/wiki/Working_directory)
|
||||
## from the environment. File operations on relative [Path]s are relative to this directory.
|
||||
|
@ -33,31 +32,40 @@ var = \name ->
|
|||
|> Effect.map (\result -> Result.mapErr result \{} -> VarNotFound)
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
# ## Reads the given environment variable and attempts to decode it.
|
||||
# ##
|
||||
# ## The type being decoded into will be determined by type inference. For example,
|
||||
# ## if this ends up being used like a `Task U16 …` then the environment variable
|
||||
# ## will be decoded as a string representation of a `U16`.
|
||||
# ##
|
||||
# ## getU16Var : Str -> Task U16 [VarNotFound, DecodeErr DecodeError]* [Env]*
|
||||
# ## getU16Var = \var -> Env.decode var
|
||||
# ## # If the environment contains a variable NUM_THINGS=123, then calling
|
||||
# ## # (getU16Var "NUM_THINGS") would return a task which succeeds with the U16 number 123.
|
||||
# ## #
|
||||
# ## # However, if the NUM_THINGS environment variable was set to 1234567, then
|
||||
# ## # (getU16Var "NUM_THINGS") would fail because that number is too big to fit in a U16.
|
||||
# ##
|
||||
# ## Supported types:
|
||||
# ## - strings
|
||||
# ## - numbers, as long as they contain only numeric digits, up to one `.`, and an optional `-` at the front for negative numbers
|
||||
# ## - comma-separated lists (of either strings or numbers), as long as there are no spaces after the commas
|
||||
# ##
|
||||
# ## Trying to decode into any other types will always fail with a `DecodeErr`.
|
||||
# decode : Str -> Task val [VarNotFound, DecodeErr DecodeError]* [Env]*
|
||||
# | val has Decode
|
||||
# decode = \var ->
|
||||
# Effect.envVar var
|
||||
# |> InternalTask.fromEffect
|
||||
## Reads the given environment variable and attempts to decode it.
|
||||
##
|
||||
## The type being decoded into will be determined by type inference. For example,
|
||||
## if this ends up being used like a `Task U16 …` then the environment variable
|
||||
## will be decoded as a string representation of a `U16`.
|
||||
##
|
||||
## getU16Var : Str -> Task U16 [VarNotFound, DecodeErr DecodeError]* [Env]*
|
||||
## getU16Var = \var -> Env.decode var
|
||||
## # If the environment contains a variable NUM_THINGS=123, then calling
|
||||
## # (getU16Var "NUM_THINGS") would return a task which succeeds with the U16 number 123.
|
||||
## #
|
||||
## # However, if the NUM_THINGS environment variable was set to 1234567, then
|
||||
## # (getU16Var "NUM_THINGS") would fail because that number is too big to fit in a U16.
|
||||
##
|
||||
## Supported types:
|
||||
## - strings
|
||||
## - numbers, as long as they contain only numeric digits, up to one `.`, and an optional `-` at the front for negative numbers
|
||||
## - comma-separated lists (of either strings or numbers), as long as there are no spaces after the commas
|
||||
##
|
||||
## Trying to decode into any other types will always fail with a `DecodeErr`.
|
||||
decode : Str -> Task val [VarNotFound, DecodeErr DecodeError]* [Env]* | val has Decoding
|
||||
decode = \name ->
|
||||
Effect.envVar name
|
||||
|> Effect.map
|
||||
(
|
||||
\result ->
|
||||
result
|
||||
|> Result.mapErr (\{} -> VarNotFound)
|
||||
|> Result.try
|
||||
(\varStr ->
|
||||
Decode.fromBytes (Str.toUtf8 varStr) (EnvDecoding.format {})
|
||||
|> Result.mapErr (\_ -> DecodeErr TooShort)))
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
## Reads all the process's environment variables into a [Dict].
|
||||
##
|
||||
## If any key or value contains invalid Unicode, the [Unicode replacement character](https://unicode.org/glossary/#replacement_character)
|
||||
|
|
95
examples/interactive/cli-platform/EnvDecoding.roc
Normal file
95
examples/interactive/cli-platform/EnvDecoding.roc
Normal file
|
@ -0,0 +1,95 @@
|
|||
interface EnvDecoding exposes [EnvFormat, format] imports []
|
||||
|
||||
EnvFormat := {} has [
|
||||
DecoderFormatting {
|
||||
u8: envU8,
|
||||
u16: envU16,
|
||||
u32: envU32,
|
||||
u64: envU64,
|
||||
u128: envU128,
|
||||
i8: envI8,
|
||||
i16: envI16,
|
||||
i32: envI32,
|
||||
i64: envI64,
|
||||
i128: envI128,
|
||||
f32: envF32,
|
||||
f64: envF64,
|
||||
dec: envDec,
|
||||
bool: envBool,
|
||||
string: envString,
|
||||
list: envList,
|
||||
record: envRecord,
|
||||
},
|
||||
]
|
||||
|
||||
format : {} -> EnvFormat
|
||||
format = \{} -> @EnvFormat {}
|
||||
|
||||
decodeBytesToNum = \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 }
|
||||
|
||||
Err _ -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
envU8 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU8
|
||||
envU16 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU16
|
||||
envU32 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU32
|
||||
envU64 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU64
|
||||
envU128 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU128
|
||||
envI8 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI8
|
||||
envI16 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI16
|
||||
envI32 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI32
|
||||
envI64 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI64
|
||||
envI128 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI128
|
||||
envF32 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toF32
|
||||
envF64 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toF64
|
||||
envDec = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toDec
|
||||
envBool = 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 }
|
||||
envString = Decode.custom \bytes, @EnvFormat {} ->
|
||||
when Str.fromUtf8 bytes is
|
||||
Ok s -> { result: Ok s, rest: [] }
|
||||
Err _ -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
envList = \decodeElem -> 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.
|
||||
decodeElems = \allBytes, accum ->
|
||||
{ toParse, remainder } =
|
||||
when List.splitFirst allBytes (Num.toU8 ',') is
|
||||
Ok { before, after } ->
|
||||
{ toParse: before, remainder: Some after }
|
||||
|
||||
Err NotFound ->
|
||||
{ toParse: allBytes, remainder: None }
|
||||
|
||||
when Decode.decodeWith toParse decodeElem (@EnvFormat {}) is
|
||||
{ result, rest } ->
|
||||
when result is
|
||||
Ok val ->
|
||||
when remainder is
|
||||
Some restBytes -> decodeElems restBytes (List.append accum val)
|
||||
None -> Done (List.append accum val)
|
||||
|
||||
Err e -> Errored e rest
|
||||
|
||||
when decodeElems 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.
|
||||
envRecord : _, (_, _ -> [Keep (Decoder _ _), Skip]), (_ -> _) -> Decoder _ _
|
||||
envRecord = \_initialState, _stepField, _finalizer -> Decode.custom \bytes, @EnvFormat {} ->
|
||||
{ result: Err TooShort, rest: bytes }
|
25
examples/interactive/env.roc
Normal file
25
examples/interactive/env.roc
Normal file
|
@ -0,0 +1,25 @@
|
|||
app "env"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Env, pf.Task, pf.Program.{ Program }]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main =
|
||||
Env.decode "EDITOR"
|
||||
|> Task.await (\editor -> Stdout.line "Your favorite editor is \(editor)!")
|
||||
|> Task.await (\{} -> Env.decode "SHLVL")
|
||||
|> Task.await
|
||||
(\lvl ->
|
||||
when lvl is
|
||||
1u8 -> Stdout.line "You're running this in a root shell!"
|
||||
n ->
|
||||
lvlStr = Num.toStr n
|
||||
|
||||
Stdout.line "Your current shell level is \(lvlStr)!")
|
||||
|> Task.await (\{} -> Env.decode "LETTERS")
|
||||
|> Task.await
|
||||
(\letters ->
|
||||
joinedLetters = Str.joinWith letters " "
|
||||
|
||||
Stdout.line "Your favorite letters are: \(joinedLetters)")
|
||||
|> Program.quick
|
Loading…
Add table
Add a link
Reference in a new issue