Merge pull request #3990 from roc-lang/pass-args-to-cli

Pass args from cli platform to app
This commit is contained in:
Richard Feldman 2022-09-14 08:43:13 -07:00 committed by GitHub
commit 21f3d4b8bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 114 additions and 53 deletions

View file

@ -21,3 +21,11 @@ jobs:
- name: execute tests with --release - name: execute tests with --release
run: /home/big-ci-user/.nix-profile/bin/nix develop -c cargo test --locked --release run: /home/big-ci-user/.nix-profile/bin/nix develop -c cargo test --locked --release
- name: upload args binary for inspection
if: always()
uses: actions/upload-artifact@v3
with:
name: failing_args_binary
path: examples/interactive/args
retention-days: 4

View file

@ -61,12 +61,18 @@ mod cli_run {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
const ALLOW_VALGRIND: bool = false; const ALLOW_VALGRIND: bool = false;
#[derive(Debug, PartialEq, Eq)]
enum Arg<'a> {
ExamplePath(&'a str),
PlainText(&'a str),
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
struct Example<'a> { struct Example<'a> {
filename: &'a str, filename: &'a str,
executable_filename: &'a str, executable_filename: &'a str,
stdin: &'a [&'a str], stdin: &'a [&'a str],
input_paths: &'a [&'a str], arguments: &'a [Arg<'a>],
expected_ending: &'a str, expected_ending: &'a str,
use_valgrind: bool, use_valgrind: bool,
} }
@ -223,7 +229,7 @@ mod cli_run {
stdin: &[&str], stdin: &[&str],
executable_filename: &str, executable_filename: &str,
flags: &[&str], flags: &[&str],
input_paths: &[&str], args: &[&Arg],
expected_ending: &str, expected_ending: &str,
) { ) {
assert!(input_paths.is_empty(), "Wasm does not support input files"); assert!(input_paths.is_empty(), "Wasm does not support input files");
@ -273,9 +279,19 @@ mod cli_run {
let file_name = example_file(dir_name, example.filename); let file_name = example_file(dir_name, example.filename);
let mut app_args: Vec<String> = vec![]; let mut app_args: Vec<String> = vec![];
for file in example.input_paths { for arg in example.arguments {
match arg {
Arg::ExamplePath(file) => {
app_args.push(example_file(dir_name, file).to_str().unwrap().to_string()); app_args.push(example_file(dir_name, file).to_str().unwrap().to_string());
} }
Arg::PlainText(arg) => {
app_args.push(arg.to_string());
}
}
}
// workaround for surgical linker issue, see PR #3990
let mut custom_flags : Vec<&str> = vec![];
match example.executable_filename { match example.executable_filename {
"form" | "hello-gui" | "breakout" | "ruby" => { "form" | "hello-gui" | "breakout" | "ruby" => {
@ -296,6 +312,9 @@ mod cli_run {
eprintln!("WARNING: skipping testing example {} because it only works in a browser!", example.filename); eprintln!("WARNING: skipping testing example {} because it only works in a browser!", example.filename);
return; return;
} }
"args" => {
custom_flags = vec![LINKER_FLAG, "legacy"];
}
_ => {} _ => {}
} }
@ -304,12 +323,13 @@ mod cli_run {
&file_name, &file_name,
example.stdin, example.stdin,
example.executable_filename, example.executable_filename,
&[], &custom_flags,
&app_args, &app_args,
example.expected_ending, example.expected_ending,
example.use_valgrind, example.use_valgrind,
); );
custom_flags.push(OPTIMIZE_FLAG);
// This is mostly because the false interpreter is still very slow - // This is mostly because the false interpreter is still very slow -
// 25s for the cli tests is just not acceptable during development! // 25s for the cli tests is just not acceptable during development!
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
@ -317,7 +337,7 @@ mod cli_run {
&file_name, &file_name,
example.stdin, example.stdin,
example.executable_filename, example.executable_filename,
&[OPTIMIZE_FLAG], &custom_flags,
&app_args, &app_args,
example.expected_ending, example.expected_ending,
example.use_valgrind, example.use_valgrind,
@ -368,7 +388,7 @@ mod cli_run {
filename: "main.roc", filename: "main.roc",
executable_filename: "helloWorld", executable_filename: "helloWorld",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"Hello, World!\n", expected_ending:"Hello, World!\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -376,7 +396,7 @@ mod cli_run {
filename: "main.roc", filename: "main.roc",
executable_filename: "rocLovesPlatforms", executable_filename: "rocLovesPlatforms",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"Which platform am I running on now?\n", expected_ending:"Which platform am I running on now?\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -387,7 +407,7 @@ mod cli_run {
// filename: "rocLovesC.roc", // filename: "rocLovesC.roc",
// executable_filename: "rocLovesC", // executable_filename: "rocLovesC",
// stdin: &[], // stdin: &[],
// input_paths: &[], // arguments: &[],
// expected_ending:"Roc <3 C!\n", // expected_ending:"Roc <3 C!\n",
// use_valgrind: true, // use_valgrind: true,
// }, // },
@ -395,7 +415,7 @@ mod cli_run {
filename: "rocLovesRust.roc", filename: "rocLovesRust.roc",
executable_filename: "rocLovesRust", executable_filename: "rocLovesRust",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"Roc <3 Rust!\n", expected_ending:"Roc <3 Rust!\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -403,7 +423,7 @@ mod cli_run {
filename: "rocLovesSwift.roc", filename: "rocLovesSwift.roc",
executable_filename: "rocLovesSwift", executable_filename: "rocLovesSwift",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"Roc <3 Swift!\n", expected_ending:"Roc <3 Swift!\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -411,7 +431,7 @@ mod cli_run {
filename: "rocLovesWebAssembly.roc", filename: "rocLovesWebAssembly.roc",
executable_filename: "rocLovesWebAssembly", executable_filename: "rocLovesWebAssembly",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"Roc <3 Web Assembly!\n", expected_ending:"Roc <3 Web Assembly!\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -419,7 +439,7 @@ mod cli_run {
filename: "rocLovesZig.roc", filename: "rocLovesZig.roc",
executable_filename: "rocLovesZig", executable_filename: "rocLovesZig",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"Roc <3 Zig!\n", expected_ending:"Roc <3 Zig!\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -427,7 +447,7 @@ mod cli_run {
filename: "main.roc", filename: "main.roc",
executable_filename: "libhello", executable_filename: "libhello",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"", expected_ending:"",
use_valgrind: true, use_valgrind: true,
}, },
@ -435,7 +455,7 @@ mod cli_run {
filename: "fibonacci.roc", filename: "fibonacci.roc",
executable_filename: "fibonacci", executable_filename: "fibonacci",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending:"55\n", expected_ending:"55\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -443,7 +463,7 @@ mod cli_run {
filename: "Hello.roc", filename: "Hello.roc",
executable_filename: "hello-gui", executable_filename: "hello-gui",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "", expected_ending: "",
use_valgrind: false, use_valgrind: false,
}, },
@ -451,7 +471,7 @@ mod cli_run {
filename: "breakout.roc", filename: "breakout.roc",
executable_filename: "breakout", executable_filename: "breakout",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "", expected_ending: "",
use_valgrind: false, use_valgrind: false,
}, },
@ -459,7 +479,7 @@ mod cli_run {
filename: "quicksort.roc", filename: "quicksort.roc",
executable_filename: "quicksort", executable_filename: "quicksort",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n", 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, use_valgrind: true,
}, },
@ -467,15 +487,23 @@ mod cli_run {
// filename: "Quicksort.roc", // filename: "Quicksort.roc",
// executable_filename: "quicksort", // executable_filename: "quicksort",
// stdin: &[], // stdin: &[],
// input_paths: &[], // arguments: &[],
// expected_ending: "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n", // 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, // use_valgrind: true,
// }, // },
args:"interactive" => Example {
filename: "args.roc",
executable_filename: "args",
stdin: &[],
arguments: &[Arg::PlainText("hello"), Arg::PlainText("world")],
expected_ending: "hello world\n",
use_valgrind: false,
},
effects:"interactive" => Example { effects:"interactive" => Example {
filename: "effects.roc", filename: "effects.roc",
executable_filename: "effects", executable_filename: "effects",
stdin: &["hi there!"], stdin: &["hi there!"],
input_paths: &[], arguments: &[],
expected_ending: "hi there!\nIt is known\n", expected_ending: "hi there!\nIt is known\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -483,7 +511,7 @@ mod cli_run {
// filename: "Main.roc", // filename: "Main.roc",
// executable_filename: "tea-example", // executable_filename: "tea-example",
// stdin: &[], // stdin: &[],
// input_paths: &[], // arguments: &[],
// expected_ending: "", // expected_ending: "",
// use_valgrind: true, // use_valgrind: true,
// }, // },
@ -491,7 +519,7 @@ mod cli_run {
filename: "form.roc", filename: "form.roc",
executable_filename: "form", executable_filename: "form",
stdin: &["Giovanni\n", "Giorgio\n"], stdin: &["Giovanni\n", "Giorgio\n"],
input_paths: &[], arguments: &[],
expected_ending: "Hi, Giovanni Giorgio! 👋\n", expected_ending: "Hi, Giovanni Giorgio! 👋\n",
use_valgrind: false, use_valgrind: false,
}, },
@ -499,7 +527,7 @@ mod cli_run {
filename: "tui.roc", filename: "tui.roc",
executable_filename: "tui", executable_filename: "tui",
stdin: &["foo\n"], // NOTE: adding more lines leads to memory leaks stdin: &["foo\n"], // NOTE: adding more lines leads to memory leaks
input_paths: &[], arguments: &[],
expected_ending: "Hello Worldfoo!\n", expected_ending: "Hello Worldfoo!\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -507,7 +535,7 @@ mod cli_run {
// filename: "Main.roc", // filename: "Main.roc",
// executable_filename: "custom-malloc-example", // executable_filename: "custom-malloc-example",
// stdin: &[], // stdin: &[],
// input_paths: &[], // arguments: &[],
// expected_ending: "ms!\nThe list was small!\n", // expected_ending: "ms!\nThe list was small!\n",
// use_valgrind: true, // use_valgrind: true,
// }, // },
@ -515,7 +543,7 @@ mod cli_run {
// filename: "Main.roc", // filename: "Main.roc",
// executable_filename: "task-example", // executable_filename: "task-example",
// stdin: &[], // stdin: &[],
// input_paths: &[], // arguments: &[],
// expected_ending: "successfully wrote to file\n", // expected_ending: "successfully wrote to file\n",
// use_valgrind: true, // use_valgrind: true,
// }, // },
@ -524,7 +552,7 @@ mod cli_run {
filename: "False.roc", filename: "False.roc",
executable_filename: "false", executable_filename: "false",
stdin: &[], stdin: &[],
input_paths: &["examples/hello.false"], arguments: &[Arg::ExamplePath("examples/hello.false")],
expected_ending:"Hello, World!\n", expected_ending:"Hello, World!\n",
use_valgrind: false, use_valgrind: false,
} }
@ -534,7 +562,7 @@ mod cli_run {
filename: "static-site.roc", filename: "static-site.roc",
executable_filename: "static-site", executable_filename: "static-site",
stdin: &[], stdin: &[],
input_paths: &["input", "output"], arguments: &[Arg::ExamplePath("input"), Arg::ExamplePath("output")],
expected_ending: "Processed 3 files with 3 successes and 0 errors\n", expected_ending: "Processed 3 files with 3 successes and 0 errors\n",
use_valgrind: false, use_valgrind: false,
} }
@ -564,9 +592,16 @@ mod cli_run {
let mut ran_without_optimizations = false; let mut ran_without_optimizations = false;
let mut app_args: Vec<String> = vec![]; let mut app_args: Vec<String> = vec![];
for file in benchmark.input_paths { for arg in benchmark.arguments {
match arg {
Arg::ExamplePath(file) => {
app_args.push(examples_dir("benchmarks").join(file).to_str().unwrap().to_string()); app_args.push(examples_dir("benchmarks").join(file).to_str().unwrap().to_string());
} }
Arg::PlainText(arg) => {
app_args.push(arg.to_string());
}
}
}
BENCHMARKS_BUILD_PLATFORM.call_once( || { BENCHMARKS_BUILD_PLATFORM.call_once( || {
// Check with and without optimizations // Check with and without optimizations
@ -717,7 +752,7 @@ mod cli_run {
filename: "NQueens.roc", filename: "NQueens.roc",
executable_filename: "nqueens", executable_filename: "nqueens",
stdin: &["6"], stdin: &["6"],
input_paths: &[], arguments: &[],
expected_ending: "4\n", expected_ending: "4\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -725,7 +760,7 @@ mod cli_run {
filename: "CFold.roc", filename: "CFold.roc",
executable_filename: "cfold", executable_filename: "cfold",
stdin: &["3"], stdin: &["3"],
input_paths: &[], arguments: &[],
expected_ending: "11 & 11\n", expected_ending: "11 & 11\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -733,7 +768,7 @@ mod cli_run {
filename: "Deriv.roc", filename: "Deriv.roc",
executable_filename: "deriv", executable_filename: "deriv",
stdin: &["2"], stdin: &["2"],
input_paths: &[], arguments: &[],
expected_ending: "1 count: 6\n2 count: 22\n", expected_ending: "1 count: 6\n2 count: 22\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -741,7 +776,7 @@ mod cli_run {
filename: "RBTreeCk.roc", filename: "RBTreeCk.roc",
executable_filename: "rbtree-ck", executable_filename: "rbtree-ck",
stdin: &["100"], stdin: &["100"],
input_paths: &[], arguments: &[],
expected_ending: "10\n", expected_ending: "10\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -749,7 +784,7 @@ mod cli_run {
filename: "RBTreeInsert.roc", filename: "RBTreeInsert.roc",
executable_filename: "rbtree-insert", executable_filename: "rbtree-insert",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "Node Black 0 {} Empty Empty\n", expected_ending: "Node Black 0 {} Empty Empty\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -757,7 +792,7 @@ mod cli_run {
// filename: "RBTreeDel.roc", // filename: "RBTreeDel.roc",
// executable_filename: "rbtree-del", // executable_filename: "rbtree-del",
// stdin: &["420"], // stdin: &["420"],
// input_paths: &[], // arguments: &[],
// expected_ending: "30\n", // expected_ending: "30\n",
// use_valgrind: true, // use_valgrind: true,
// }, // },
@ -765,7 +800,7 @@ mod cli_run {
filename: "TestAStar.roc", filename: "TestAStar.roc",
executable_filename: "test-astar", executable_filename: "test-astar",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "True\n", expected_ending: "True\n",
use_valgrind: false, use_valgrind: false,
}, },
@ -773,7 +808,7 @@ mod cli_run {
filename: "TestBase64.roc", filename: "TestBase64.roc",
executable_filename: "test-base64", executable_filename: "test-base64",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "encoded: SGVsbG8gV29ybGQ=\ndecoded: Hello World\n", expected_ending: "encoded: SGVsbG8gV29ybGQ=\ndecoded: Hello World\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -781,7 +816,7 @@ mod cli_run {
filename: "Closure.roc", filename: "Closure.roc",
executable_filename: "closure", executable_filename: "closure",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "", expected_ending: "",
use_valgrind: false, use_valgrind: false,
}, },
@ -789,7 +824,7 @@ mod cli_run {
filename: "Issue2279.roc", filename: "Issue2279.roc",
executable_filename: "issue2279", executable_filename: "issue2279",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "Hello, world!\n", expected_ending: "Hello, world!\n",
use_valgrind: true, use_valgrind: true,
}, },
@ -797,7 +832,7 @@ mod cli_run {
filename: "QuicksortApp.roc", filename: "QuicksortApp.roc",
executable_filename: "quicksortapp", executable_filename: "quicksortapp",
stdin: &[], stdin: &[],
input_paths: &[], arguments: &[],
expected_ending: "todo put the correct quicksort answer here", expected_ending: "todo put the correct quicksort answer here",
use_valgrind: true, use_valgrind: true,
}, },

View file

@ -3,7 +3,7 @@ app "type-error"
imports [pf.Stdout.{ line }, pf.Task.{ await }] imports [pf.Stdout.{ line }, pf.Task.{ await }]
provides [main] to pf provides [main] to pf
main = main = \_args ->
_ <- await (line "a") _ <- await (line "a")
_ <- await (line "b") _ <- await (line "b")
_ <- await (line "c") _ <- await (line "c")

View file

@ -1,3 +1,4 @@
args
countdown countdown
echo echo
effects effects

View file

@ -0,0 +1,11 @@
app "args"
packages { pf: "cli-platform/main.roc" }
imports [pf.Stdout, pf.Task]
provides [main] to pf
main : List Str -> Task.Task {} [] [Write [Stdout]]
main = \args ->
_ <- Task.await (Stdout.line "Here are the args I got from the command line:")
joinedArgs = Str.joinWith args " "
Stdout.line joinedArgs

View file

@ -1,9 +1,9 @@
platform "cli" platform "cli"
requires {} { main : Task {} [] * } requires {} { main : List Str -> Task {} [] * }
exposes [] exposes []
packages {} packages {}
imports [Task.{ Task }, InternalTask, Effect.{ Effect }] imports [Task.{ Task }, InternalTask, Effect.{ Effect }]
provides [mainForHost] provides [mainForHost]
mainForHost : Effect (Result {} []) as Fx mainForHost : List Str -> Effect (Result {} []) as Fx
mainForHost = InternalTask.toEffect main mainForHost = \args -> InternalTask.toEffect (main args)

View file

@ -9,6 +9,7 @@ use core::mem::MaybeUninit;
use glue::Metadata; use glue::Metadata;
use libc; use libc;
use roc_std::{RocList, RocResult, RocStr}; use roc_std::{RocList, RocResult, RocStr};
use std::borrow::Borrow;
use std::ffi::{CStr, OsStr}; use std::ffi::{CStr, OsStr};
use std::fs::File; use std::fs::File;
use std::os::raw::c_char; use std::os::raw::c_char;
@ -20,7 +21,7 @@ use file_glue::WriteErr;
extern "C" { extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"] #[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(output: *mut u8); fn roc_main(output: *mut u8, args: *const RocList<RocStr>);
#[link_name = "roc__mainForHost_size"] #[link_name = "roc__mainForHost_size"]
fn roc_main_size() -> i64; fn roc_main_size() -> i64;
@ -84,11 +85,16 @@ pub extern "C" fn rust_main() -> i32 {
let size = unsafe { roc_main_size() } as usize; let size = unsafe { roc_main_size() } as usize;
let layout = Layout::array::<u8>(size).unwrap(); let layout = Layout::array::<u8>(size).unwrap();
// TODO: can we be more efficient about reusing the String's memory for RocStr?
let args: RocList<RocStr> = std::env::args_os()
.map(|s| RocStr::from(s.to_string_lossy().borrow()))
.collect();
unsafe { unsafe {
// TODO allocate on the stack if it's under a certain size // TODO allocate on the stack if it's under a certain size
let buffer = std::alloc::alloc(layout); let buffer = std::alloc::alloc(layout);
roc_main(buffer); roc_main(buffer, &args);
let result = call_the_closure(buffer); let result = call_the_closure(buffer);

View file

@ -3,7 +3,7 @@ app "countdown"
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop, succeed }] imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop, succeed }]
provides [main] to pf provides [main] to pf
main = main = \_args ->
_ <- await (Stdout.line "\nLet's count down from 10 together - all you have to do is press <ENTER>.") _ <- await (Stdout.line "\nLet's count down from 10 together - all you have to do is press <ENTER>.")
_ <- await Stdin.line _ <- await Stdin.line
loop 10 tick loop 10 tick

View file

@ -3,8 +3,8 @@ app "echo"
imports [pf.Stdin, pf.Stdout, pf.Task] imports [pf.Stdin, pf.Stdout, pf.Task]
provides [main] to pf provides [main] to pf
main : Task.Task {} [] [Read [Stdin], Write [Stdout]] main : List Str -> Task.Task {} [] [Read [Stdin], Write [Stdout]]
main = main = \_args ->
_ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂") _ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂")
Task.loop {} (\_ -> Task.map tick Step) Task.loop {} (\_ -> Task.map tick Step)

View file

@ -3,8 +3,8 @@ app "form"
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }] imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }]
provides [main] to pf provides [main] to pf
main : Task {} * [Read [Stdin], Write [Stdout]] main : List Str -> Task {} * [Read [Stdin], Write [Stdout]]
main = main = \_args ->
_ <- await (Stdout.line "What's your first name?") _ <- await (Stdout.line "What's your first name?")
firstName <- await Stdin.line firstName <- await Stdin.line
_ <- await (Stdout.line "What's your last name?") _ <- await (Stdout.line "What's your last name?")

View file

@ -3,8 +3,8 @@ app "http-get"
imports [pf.Http, pf.Task, pf.Stdin, pf.Stdout] imports [pf.Http, pf.Task, pf.Stdin, pf.Stdout]
provides [main] to pf provides [main] to pf
main : Task.Task {} [] [Read [Stdin], Write [Stdout], Network [Http]] main : List Str -> Task.Task {} [] [Read [Stdin], Write [Stdout], Network [Http]]
main = main = \_args ->
_ <- Task.await (Stdout.line "Please enter a URL to fetch") _ <- Task.await (Stdout.line "Please enter a URL to fetch")
url <- Task.await Stdin.line url <- Task.await Stdin.line