mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
Merge pull request #3920 from roc-lang/static-site-gen
Static site generator example
This commit is contained in:
commit
bfc00a4b94
@ -20,7 +20,7 @@ mod cli_run {
|
||||
use roc_test_utils::assert_multiline_str_eq;
|
||||
use serial_test::serial;
|
||||
use std::iter;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
@ -66,7 +66,7 @@ mod cli_run {
|
||||
filename: &'a str,
|
||||
executable_filename: &'a str,
|
||||
stdin: &'a [&'a str],
|
||||
input_file: Option<&'a str>,
|
||||
input_paths: &'a [&'a str],
|
||||
expected_ending: &'a str,
|
||||
use_valgrind: bool,
|
||||
}
|
||||
@ -93,24 +93,16 @@ mod cli_run {
|
||||
file: &'a Path,
|
||||
args: I,
|
||||
stdin: &[&str],
|
||||
opt_input_file: Option<PathBuf>,
|
||||
app_args: &[String],
|
||||
) -> Out {
|
||||
let compile_out = if let Some(input_file) = opt_input_file {
|
||||
run_roc(
|
||||
// converting these all to String avoids lifetime issues
|
||||
args.into_iter().map(|arg| arg.to_string()).chain([
|
||||
file.to_str().unwrap().to_string(),
|
||||
"--".to_string(),
|
||||
input_file.to_str().unwrap().to_string(),
|
||||
]),
|
||||
stdin,
|
||||
)
|
||||
} else {
|
||||
run_roc(
|
||||
args.into_iter().chain(iter::once(file.to_str().unwrap())),
|
||||
stdin,
|
||||
)
|
||||
};
|
||||
let compile_out = run_roc(
|
||||
// converting these all to String avoids lifetime issues
|
||||
args.into_iter()
|
||||
.map(|arg| arg.to_string())
|
||||
.chain([file.to_str().unwrap().to_string(), "--".to_string()])
|
||||
.chain(app_args.iter().cloned()),
|
||||
stdin,
|
||||
);
|
||||
|
||||
// If there is any stderr, it should be reporting the runtime and that's it!
|
||||
if !(compile_out.stderr.is_empty()
|
||||
@ -132,7 +124,7 @@ mod cli_run {
|
||||
stdin: &[&str],
|
||||
executable_filename: &str,
|
||||
flags: &[&str],
|
||||
opt_input_file: Option<PathBuf>,
|
||||
app_args: &[String],
|
||||
expected_ending: &str,
|
||||
use_valgrind: bool,
|
||||
) {
|
||||
@ -154,24 +146,17 @@ mod cli_run {
|
||||
|
||||
let out = match cli_mode {
|
||||
CliMode::RocBuild => {
|
||||
run_roc_on(file, iter::once(CMD_BUILD).chain(flags.clone()), &[], None);
|
||||
run_roc_on(file, iter::once(CMD_BUILD).chain(flags.clone()), &[], &[]);
|
||||
|
||||
if use_valgrind && ALLOW_VALGRIND {
|
||||
let (valgrind_out, raw_xml) = if let Some(ref input_file) = opt_input_file {
|
||||
run_with_valgrind(
|
||||
stdin.iter().copied(),
|
||||
&[
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
input_file.clone().to_str().unwrap(),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
run_with_valgrind(
|
||||
stdin.iter().copied(),
|
||||
&[file.with_file_name(executable_filename).to_str().unwrap()],
|
||||
)
|
||||
};
|
||||
|
||||
let mut valgrind_args = vec![file
|
||||
.with_file_name(executable_filename)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()];
|
||||
valgrind_args.extend(app_args.iter().cloned());
|
||||
let (valgrind_out, raw_xml) =
|
||||
run_with_valgrind(stdin.iter().copied(), &valgrind_args);
|
||||
if valgrind_out.status.success() {
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
||||
panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr);
|
||||
@ -207,26 +192,20 @@ mod cli_run {
|
||||
}
|
||||
|
||||
valgrind_out
|
||||
} else if let Some(ref input_file) = opt_input_file {
|
||||
run_cmd(
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
stdin.iter().copied(),
|
||||
&[input_file.to_str().unwrap()],
|
||||
)
|
||||
} else {
|
||||
run_cmd(
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
stdin.iter().copied(),
|
||||
&[],
|
||||
app_args,
|
||||
)
|
||||
}
|
||||
}
|
||||
CliMode::Roc => run_roc_on(file, flags.clone(), stdin, opt_input_file.clone()),
|
||||
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,
|
||||
opt_input_file.clone(),
|
||||
app_args,
|
||||
),
|
||||
};
|
||||
|
||||
@ -247,10 +226,10 @@ mod cli_run {
|
||||
stdin: &[&str],
|
||||
executable_filename: &str,
|
||||
flags: &[&str],
|
||||
input_file: Option<PathBuf>,
|
||||
input_paths: &[&str],
|
||||
expected_ending: &str,
|
||||
) {
|
||||
assert_eq!(input_file, None, "Wasm does not support input files");
|
||||
assert!(input_paths.is_empty(), "Wasm does not support input files");
|
||||
let mut flags = flags.to_vec();
|
||||
flags.push(concatcp!(TARGET_FLAG, "=wasm32"));
|
||||
|
||||
@ -296,12 +275,17 @@ mod cli_run {
|
||||
let example = $example;
|
||||
let file_name = example_file(dir_name, example.filename);
|
||||
|
||||
let mut app_args: Vec<String> = vec![];
|
||||
for file in example.input_paths {
|
||||
app_args.push(example_file(dir_name, file).to_str().unwrap().to_string());
|
||||
}
|
||||
|
||||
match example.executable_filename {
|
||||
"form" | "hello-gui" | "breakout" | "ruby" => {
|
||||
// Since these require things the build system often doesn't have
|
||||
// (e.g. GUIs open a window, Ruby needs ruby installed, WASM needs a browser)
|
||||
// we do `roc build` on them but don't run them.
|
||||
run_roc_on(&file_name, [CMD_BUILD, OPTIMIZE_FLAG], &[], None);
|
||||
run_roc_on(&file_name, [CMD_BUILD, OPTIMIZE_FLAG], &[], &[]);
|
||||
return;
|
||||
}
|
||||
"rocLovesSwift" => {
|
||||
@ -324,7 +308,7 @@ mod cli_run {
|
||||
example.stdin,
|
||||
example.executable_filename,
|
||||
&[],
|
||||
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
||||
&app_args,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
);
|
||||
@ -337,7 +321,7 @@ mod cli_run {
|
||||
example.stdin,
|
||||
example.executable_filename,
|
||||
&[OPTIMIZE_FLAG],
|
||||
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
||||
&app_args,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
);
|
||||
@ -350,7 +334,7 @@ mod cli_run {
|
||||
example.stdin,
|
||||
example.executable_filename,
|
||||
&[LINKER_FLAG, "legacy"],
|
||||
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
||||
&app_args,
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
);
|
||||
@ -387,7 +371,7 @@ mod cli_run {
|
||||
filename: "main.roc",
|
||||
executable_filename: "helloWorld",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"Hello, World!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -395,7 +379,7 @@ mod cli_run {
|
||||
filename: "main.roc",
|
||||
executable_filename: "rocLovesPlatforms",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"Which platform am I running on now?\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -406,7 +390,7 @@ mod cli_run {
|
||||
// filename: "rocLovesC.roc",
|
||||
// executable_filename: "rocLovesC",
|
||||
// stdin: &[],
|
||||
// input_file: None,
|
||||
// input_paths: &[],
|
||||
// expected_ending:"Roc <3 C!\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
@ -414,7 +398,7 @@ mod cli_run {
|
||||
filename: "rocLovesRust.roc",
|
||||
executable_filename: "rocLovesRust",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"Roc <3 Rust!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -422,7 +406,7 @@ mod cli_run {
|
||||
filename: "rocLovesSwift.roc",
|
||||
executable_filename: "rocLovesSwift",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"Roc <3 Swift!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -430,7 +414,7 @@ mod cli_run {
|
||||
filename: "rocLovesWebAssembly.roc",
|
||||
executable_filename: "rocLovesWebAssembly",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"Roc <3 Web Assembly!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -438,7 +422,7 @@ mod cli_run {
|
||||
filename: "rocLovesZig.roc",
|
||||
executable_filename: "rocLovesZig",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"Roc <3 Zig!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -446,7 +430,7 @@ mod cli_run {
|
||||
filename: "main.roc",
|
||||
executable_filename: "libhello",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -454,7 +438,7 @@ mod cli_run {
|
||||
filename: "fibonacci.roc",
|
||||
executable_filename: "fibonacci",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending:"55\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -462,7 +446,7 @@ mod cli_run {
|
||||
filename: "Hello.roc",
|
||||
executable_filename: "hello-gui",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
@ -470,7 +454,7 @@ mod cli_run {
|
||||
filename: "breakout.roc",
|
||||
executable_filename: "breakout",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
@ -478,7 +462,7 @@ mod cli_run {
|
||||
filename: "quicksort.roc",
|
||||
executable_filename: "quicksort",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
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,
|
||||
},
|
||||
@ -486,7 +470,7 @@ mod cli_run {
|
||||
// filename: "Quicksort.roc",
|
||||
// executable_filename: "quicksort",
|
||||
// stdin: &[],
|
||||
// input_file: None,
|
||||
// input_paths: &[],
|
||||
// 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,
|
||||
// },
|
||||
@ -494,7 +478,7 @@ mod cli_run {
|
||||
filename: "effects.roc",
|
||||
executable_filename: "effects",
|
||||
stdin: &["hi there!"],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "hi there!\nIt is known\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -502,7 +486,7 @@ mod cli_run {
|
||||
// filename: "Main.roc",
|
||||
// executable_filename: "tea-example",
|
||||
// stdin: &[],
|
||||
// input_file: None,
|
||||
// input_paths: &[],
|
||||
// expected_ending: "",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
@ -510,7 +494,7 @@ mod cli_run {
|
||||
filename: "form.roc",
|
||||
executable_filename: "form",
|
||||
stdin: &["Giovanni\n", "Giorgio\n"],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "Hi, Giovanni Giorgio! 👋\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
@ -518,7 +502,7 @@ mod cli_run {
|
||||
filename: "tui.roc",
|
||||
executable_filename: "tui",
|
||||
stdin: &["foo\n"], // NOTE: adding more lines leads to memory leaks
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "Hello Worldfoo!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -526,7 +510,7 @@ mod cli_run {
|
||||
// filename: "Main.roc",
|
||||
// executable_filename: "custom-malloc-example",
|
||||
// stdin: &[],
|
||||
// input_file: None,
|
||||
// input_paths: &[],
|
||||
// expected_ending: "ms!\nThe list was small!\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
@ -534,7 +518,7 @@ mod cli_run {
|
||||
// filename: "Main.roc",
|
||||
// executable_filename: "task-example",
|
||||
// stdin: &[],
|
||||
// input_file: None,
|
||||
// input_paths: &[],
|
||||
// expected_ending: "successfully wrote to file\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
@ -543,11 +527,21 @@ mod cli_run {
|
||||
filename: "False.roc",
|
||||
executable_filename: "false",
|
||||
stdin: &[],
|
||||
input_file: Some("examples/hello.false"),
|
||||
input_paths: &["examples/hello.false"],
|
||||
expected_ending:"Hello, World!\n",
|
||||
use_valgrind: false,
|
||||
}
|
||||
},
|
||||
static_site_gen: "static-site-gen" => {
|
||||
Example {
|
||||
filename: "static-site.roc",
|
||||
executable_filename: "static-site",
|
||||
stdin: &[],
|
||||
input_paths: &["input", "output"],
|
||||
expected_ending: "hello.txt -> hello.html\n",
|
||||
use_valgrind: false,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
macro_rules! benchmarks {
|
||||
@ -572,6 +566,11 @@ mod cli_run {
|
||||
|
||||
let mut ran_without_optimizations = false;
|
||||
|
||||
let mut app_args: Vec<String> = vec![];
|
||||
for file in benchmark.input_paths {
|
||||
app_args.push(examples_dir("benchmarks").join(file).to_str().unwrap().to_string());
|
||||
}
|
||||
|
||||
BENCHMARKS_BUILD_PLATFORM.call_once( || {
|
||||
// Check with and without optimizations
|
||||
check_output_with_stdin(
|
||||
@ -579,7 +578,7 @@ mod cli_run {
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&[],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
&app_args,
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
@ -597,7 +596,7 @@ mod cli_run {
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&[PRECOMPILED_HOST],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
&app_args,
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
@ -608,7 +607,7 @@ mod cli_run {
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&[PRECOMPILED_HOST, OPTIMIZE_FLAG],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
&app_args,
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
@ -641,7 +640,7 @@ mod cli_run {
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&[],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.input_paths.iter().map(|file| examples_dir("benchmarks").join(file)),
|
||||
benchmark.expected_ending,
|
||||
);
|
||||
|
||||
@ -650,7 +649,7 @@ mod cli_run {
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&[OPTIMIZE_FLAG],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.input_paths.iter().map(|file| examples_dir("benchmarks").join(file)),
|
||||
benchmark.expected_ending,
|
||||
);
|
||||
}
|
||||
@ -682,7 +681,7 @@ mod cli_run {
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
[concatcp!(TARGET_FLAG, "=x86_32")],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.input_paths.iter().map(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
@ -692,7 +691,7 @@ mod cli_run {
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
[concatcp!(TARGET_FLAG, "=x86_32"), OPTIMIZE_FLAG],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.input_paths.iter().map(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
@ -721,7 +720,7 @@ mod cli_run {
|
||||
filename: "NQueens.roc",
|
||||
executable_filename: "nqueens",
|
||||
stdin: &["6"],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "4\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -729,7 +728,7 @@ mod cli_run {
|
||||
filename: "CFold.roc",
|
||||
executable_filename: "cfold",
|
||||
stdin: &["3"],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "11 & 11\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -737,7 +736,7 @@ mod cli_run {
|
||||
filename: "Deriv.roc",
|
||||
executable_filename: "deriv",
|
||||
stdin: &["2"],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "1 count: 6\n2 count: 22\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -745,7 +744,7 @@ mod cli_run {
|
||||
filename: "RBTreeCk.roc",
|
||||
executable_filename: "rbtree-ck",
|
||||
stdin: &["100"],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "10\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -753,7 +752,7 @@ mod cli_run {
|
||||
filename: "RBTreeInsert.roc",
|
||||
executable_filename: "rbtree-insert",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "Node Black 0 {} Empty Empty\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -761,7 +760,7 @@ mod cli_run {
|
||||
// filename: "RBTreeDel.roc",
|
||||
// executable_filename: "rbtree-del",
|
||||
// stdin: &["420"],
|
||||
// input_file: None,
|
||||
// input_paths: &[],
|
||||
// expected_ending: "30\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
@ -769,7 +768,7 @@ mod cli_run {
|
||||
filename: "TestAStar.roc",
|
||||
executable_filename: "test-astar",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "True\n",
|
||||
use_valgrind: false,
|
||||
},
|
||||
@ -777,7 +776,7 @@ mod cli_run {
|
||||
filename: "TestBase64.roc",
|
||||
executable_filename: "test-base64",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "encoded: SGVsbG8gV29ybGQ=\ndecoded: Hello World\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -785,7 +784,7 @@ mod cli_run {
|
||||
filename: "Closure.roc",
|
||||
executable_filename: "closure",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
@ -793,7 +792,7 @@ mod cli_run {
|
||||
filename: "Issue2279.roc",
|
||||
executable_filename: "issue2279",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "Hello, world!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -801,7 +800,7 @@ mod cli_run {
|
||||
filename: "QuicksortApp.roc",
|
||||
executable_filename: "quicksortapp",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
input_paths: &[],
|
||||
expected_ending: "todo put the correct quicksort answer here",
|
||||
use_valgrind: true,
|
||||
},
|
||||
@ -900,7 +899,7 @@ mod cli_run {
|
||||
&[],
|
||||
"multi-dep-str",
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
);
|
||||
@ -914,7 +913,7 @@ mod cli_run {
|
||||
&[],
|
||||
"multi-dep-str",
|
||||
&[OPTIMIZE_FLAG],
|
||||
None,
|
||||
&[],
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
);
|
||||
@ -928,7 +927,7 @@ mod cli_run {
|
||||
&[],
|
||||
"multi-dep-thunk",
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
);
|
||||
@ -942,7 +941,7 @@ mod cli_run {
|
||||
&[],
|
||||
"multi-dep-thunk",
|
||||
&[OPTIMIZE_FLAG],
|
||||
None,
|
||||
&[],
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
);
|
||||
|
@ -164,7 +164,7 @@ where
|
||||
pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
|
||||
cmd_name: &str,
|
||||
stdin_vals: I,
|
||||
args: &[&str],
|
||||
args: &[String],
|
||||
) -> Out {
|
||||
let mut cmd = Command::new(cmd_name);
|
||||
|
||||
@ -202,7 +202,7 @@ pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
|
||||
|
||||
pub fn run_with_valgrind<'a, I: IntoIterator<Item = &'a str>>(
|
||||
stdin_vals: I,
|
||||
args: &[&str],
|
||||
args: &[String],
|
||||
) -> (Out, String) {
|
||||
//TODO: figure out if there is a better way to get the valgrind executable.
|
||||
let mut cmd = Command::new("valgrind");
|
||||
|
@ -3726,6 +3726,10 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>(
|
||||
// Drop the return pointer the other way, if the C function returns by pointer but Roc
|
||||
// doesn't
|
||||
(RocReturn::Return, CCReturn::ByPointer) => (¶ms[1..], ¶m_types[..]),
|
||||
(RocReturn::ByPointer, CCReturn::ByPointer) => {
|
||||
// Both return by pointer but Roc puts it at the end and C puts it at the beginning
|
||||
(¶ms[1..], ¶m_types[..param_types.len() - 1])
|
||||
}
|
||||
_ => (¶ms[..], ¶m_types[..]),
|
||||
};
|
||||
|
||||
@ -3807,8 +3811,20 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>(
|
||||
},
|
||||
CCReturn::ByPointer => {
|
||||
let out_ptr = c_function.get_nth_param(0).unwrap().into_pointer_value();
|
||||
|
||||
env.builder.build_store(out_ptr, value);
|
||||
match roc_return {
|
||||
RocReturn::Return => {
|
||||
env.builder.build_store(out_ptr, value);
|
||||
}
|
||||
RocReturn::ByPointer => {
|
||||
// TODO: ideally, in this case, we should pass the C return pointer directly
|
||||
// into the call_roc_function rather than forcing an extra alloca, load, and
|
||||
// store!
|
||||
let value = env
|
||||
.builder
|
||||
.build_load(value.into_pointer_value(), "load_roc_result");
|
||||
env.builder.build_store(out_ptr, value);
|
||||
}
|
||||
}
|
||||
env.builder.build_return(None);
|
||||
}
|
||||
CCReturn::Void => {
|
||||
|
2
examples/static-site-gen/.gitignore
vendored
Normal file
2
examples/static-site-gen/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/output
|
||||
/static-site
|
14
examples/static-site-gen/README.md
Normal file
14
examples/static-site-gen/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Static site generator
|
||||
|
||||
This is an example of how you might build a static site generator using Roc.
|
||||
It searches for files in the `input` directory, transforms the contents to HTML
|
||||
using a Roc function, and writes the result into the corresponding file path in
|
||||
the `output` directory.
|
||||
|
||||
To run, `cd` into this directory and run this in your terminal:
|
||||
|
||||
```bash
|
||||
roc run app.roc input/ output/
|
||||
```
|
||||
|
||||
Eventually this example could be expanded to support Markdown parsing, database connections at build time, etc.
|
3
examples/static-site-gen/input/hello.txt
Normal file
3
examples/static-site-gen/input/hello.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Hello, World!
|
||||
I am a plain text content file.
|
||||
I make a very interesting web page, I'm sure you'll agree.
|
37
examples/static-site-gen/platform/Cargo.lock
generated
Normal file
37
examples/static-site-gen/platform/Cargo.lock
generated
Normal file
@ -0,0 +1,37 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"roc_std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
23
examples/static-site-gen/platform/Cargo.toml
Normal file
23
examples/static-site-gen/platform/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "host"
|
||||
version = "0.0.1"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2021"
|
||||
|
||||
links = "app"
|
||||
|
||||
[lib]
|
||||
name = "host"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["staticlib", "rlib"]
|
||||
|
||||
[[bin]]
|
||||
name = "host"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../crates/roc_std" }
|
||||
libc = "0.2"
|
||||
|
||||
[workspace]
|
353
examples/static-site-gen/platform/Html.roc
Normal file
353
examples/static-site-gen/platform/Html.roc
Normal file
@ -0,0 +1,353 @@
|
||||
interface Html
|
||||
exposes [
|
||||
Node,
|
||||
Attribute,
|
||||
render,
|
||||
renderWithoutDocType,
|
||||
element,
|
||||
text,
|
||||
attribute,
|
||||
html,
|
||||
base,
|
||||
head,
|
||||
link,
|
||||
meta,
|
||||
style,
|
||||
title,
|
||||
body,
|
||||
address,
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
main,
|
||||
nav,
|
||||
section,
|
||||
blockquote,
|
||||
dd,
|
||||
div,
|
||||
dl,
|
||||
dt,
|
||||
figcaption,
|
||||
figure,
|
||||
hr,
|
||||
li,
|
||||
menu,
|
||||
ol,
|
||||
p,
|
||||
pre,
|
||||
ul,
|
||||
a,
|
||||
abbr,
|
||||
b,
|
||||
bdi,
|
||||
bdo,
|
||||
br,
|
||||
cite,
|
||||
code,
|
||||
data,
|
||||
dfn,
|
||||
em,
|
||||
i,
|
||||
kbd,
|
||||
mark,
|
||||
q,
|
||||
rp,
|
||||
rt,
|
||||
ruby,
|
||||
s,
|
||||
samp,
|
||||
small,
|
||||
span,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
time,
|
||||
u,
|
||||
var,
|
||||
wbr,
|
||||
area,
|
||||
audio,
|
||||
img,
|
||||
map,
|
||||
track,
|
||||
video,
|
||||
embed,
|
||||
iframe,
|
||||
object,
|
||||
picture,
|
||||
portal,
|
||||
source,
|
||||
svg,
|
||||
math,
|
||||
canvas,
|
||||
noscript,
|
||||
script,
|
||||
del,
|
||||
ins,
|
||||
caption,
|
||||
col,
|
||||
colgroup,
|
||||
table,
|
||||
tbody,
|
||||
td,
|
||||
tfoot,
|
||||
th,
|
||||
thead,
|
||||
tr,
|
||||
button,
|
||||
datalist,
|
||||
fieldset,
|
||||
form,
|
||||
input,
|
||||
label,
|
||||
legend,
|
||||
meter,
|
||||
optgroup,
|
||||
option,
|
||||
output,
|
||||
progress,
|
||||
select,
|
||||
textarea,
|
||||
details,
|
||||
dialog,
|
||||
summary,
|
||||
slot,
|
||||
template,
|
||||
]
|
||||
imports [Html.Attributes]
|
||||
|
||||
Node : [Text Str, Element Str Nat (List Attribute) (List Node)]
|
||||
|
||||
Attribute : Html.Attributes.Attribute
|
||||
|
||||
attribute : Str -> (Str -> Attribute)
|
||||
attribute = Html.Attributes.attribute
|
||||
|
||||
text : Str -> Node
|
||||
text = Text
|
||||
|
||||
## Define a non-standard HTML Element
|
||||
##
|
||||
## You can use this to add elements that are not already supported.
|
||||
## For example, you could bring back the obsolete <blink> element,
|
||||
## and add some 90's nostalgia to your web page!
|
||||
##
|
||||
## blink : List Attribute, List Node -> Node
|
||||
## blink = element "blink"
|
||||
##
|
||||
## html = blink [] [ text "This text is blinking!" ]
|
||||
##
|
||||
element : Str -> (List Attribute, List Node -> Node)
|
||||
element = \tagName ->
|
||||
\attrs, children ->
|
||||
# While building the node tree, calculate the size of Str it will render to
|
||||
withTag = 2 * (3 + Str.countUtf8Bytes tagName)
|
||||
withAttrs = List.walk attrs withTag \acc, Attribute name val ->
|
||||
acc + Str.countUtf8Bytes name + Str.countUtf8Bytes val + 4
|
||||
totalSize = List.walk children withAttrs \acc, child ->
|
||||
acc + nodeSize child
|
||||
|
||||
Element tagName totalSize attrs children
|
||||
|
||||
# internal helper
|
||||
nodeSize : Node -> Nat
|
||||
nodeSize = \node ->
|
||||
when node is
|
||||
Text content ->
|
||||
Str.countUtf8Bytes content
|
||||
|
||||
Element _ size _ _ ->
|
||||
size
|
||||
|
||||
## Render a Node to an HTML string
|
||||
##
|
||||
## The output has no whitespace between nodes, to make it small.
|
||||
## This is intended for generating full HTML documents, so it
|
||||
## automatically adds `<!DOCTYPE html>` to the start of the string.
|
||||
## See also `renderWithoutDocType`.
|
||||
render : Node -> Str
|
||||
render = \node ->
|
||||
buffer = Str.reserve "<!DOCTYPE html>" (nodeSize node)
|
||||
|
||||
renderHelp buffer node
|
||||
|
||||
## Render a Node to a string, without a DOCTYPE tag
|
||||
renderWithoutDocType : Node -> Str
|
||||
renderWithoutDocType = \node ->
|
||||
buffer = Str.reserve "" (nodeSize node)
|
||||
|
||||
renderHelp buffer node
|
||||
|
||||
# internal helper
|
||||
renderHelp : Str, Node -> Str
|
||||
renderHelp = \buffer, node ->
|
||||
when node is
|
||||
Text content ->
|
||||
Str.concat buffer content
|
||||
|
||||
Element tagName _ attrs children ->
|
||||
withTagName = "\(buffer)<\(tagName)"
|
||||
withAttrs =
|
||||
if List.isEmpty attrs then
|
||||
withTagName
|
||||
else
|
||||
List.walk attrs "\(withTagName) " renderAttr
|
||||
withTag = Str.concat withAttrs ">"
|
||||
withChildren = List.walk children withTag renderHelp
|
||||
|
||||
"\(withChildren)</\(tagName)>"
|
||||
|
||||
# internal helper
|
||||
renderAttr : Str, Attribute -> Str
|
||||
renderAttr = \buffer, Attribute key val ->
|
||||
"\(buffer) \(key)=\"\(val)\""
|
||||
|
||||
# Main root
|
||||
html = element "html"
|
||||
|
||||
# Document metadata
|
||||
base = element "base"
|
||||
head = element "head"
|
||||
link = element "link"
|
||||
meta = element "meta"
|
||||
style = element "style"
|
||||
title = element "title"
|
||||
|
||||
# Sectioning root
|
||||
body = element "body"
|
||||
|
||||
# Content sectioning
|
||||
address = element "address"
|
||||
article = element "article"
|
||||
aside = element "aside"
|
||||
footer = element "footer"
|
||||
header = element "header"
|
||||
h1 = element "h1"
|
||||
h2 = element "h2"
|
||||
h3 = element "h3"
|
||||
h4 = element "h4"
|
||||
h5 = element "h5"
|
||||
h6 = element "h6"
|
||||
main = element "main"
|
||||
nav = element "nav"
|
||||
section = element "section"
|
||||
|
||||
# Text content
|
||||
blockquote = element "blockquote"
|
||||
dd = element "dd"
|
||||
div = element "div"
|
||||
dl = element "dl"
|
||||
dt = element "dt"
|
||||
figcaption = element "figcaption"
|
||||
figure = element "figure"
|
||||
hr = element "hr"
|
||||
li = element "li"
|
||||
menu = element "menu"
|
||||
ol = element "ol"
|
||||
p = element "p"
|
||||
pre = element "pre"
|
||||
ul = element "ul"
|
||||
|
||||
# Inline text semantics
|
||||
a = element "a"
|
||||
abbr = element "abbr"
|
||||
b = element "b"
|
||||
bdi = element "bdi"
|
||||
bdo = element "bdo"
|
||||
br = element "br"
|
||||
cite = element "cite"
|
||||
code = element "code"
|
||||
data = element "data"
|
||||
dfn = element "dfn"
|
||||
em = element "em"
|
||||
i = element "i"
|
||||
kbd = element "kbd"
|
||||
mark = element "mark"
|
||||
q = element "q"
|
||||
rp = element "rp"
|
||||
rt = element "rt"
|
||||
ruby = element "ruby"
|
||||
s = element "s"
|
||||
samp = element "samp"
|
||||
small = element "small"
|
||||
span = element "span"
|
||||
strong = element "strong"
|
||||
sub = element "sub"
|
||||
sup = element "sup"
|
||||
time = element "time"
|
||||
u = element "u"
|
||||
var = element "var"
|
||||
wbr = element "wbr"
|
||||
|
||||
# Image and multimedia
|
||||
area = element "area"
|
||||
audio = element "audio"
|
||||
img = element "img"
|
||||
map = element "map"
|
||||
track = element "track"
|
||||
video = element "video"
|
||||
|
||||
# Embedded content
|
||||
embed = element "embed"
|
||||
iframe = element "iframe"
|
||||
object = element "object"
|
||||
picture = element "picture"
|
||||
portal = element "portal"
|
||||
source = element "source"
|
||||
|
||||
# SVG and MathML
|
||||
svg = element "svg"
|
||||
math = element "math"
|
||||
|
||||
# Scripting
|
||||
canvas = element "canvas"
|
||||
noscript = element "noscript"
|
||||
script = element "script"
|
||||
|
||||
# Demarcating edits
|
||||
del = element "del"
|
||||
ins = element "ins"
|
||||
|
||||
# Table content
|
||||
caption = element "caption"
|
||||
col = element "col"
|
||||
colgroup = element "colgroup"
|
||||
table = element "table"
|
||||
tbody = element "tbody"
|
||||
td = element "td"
|
||||
tfoot = element "tfoot"
|
||||
th = element "th"
|
||||
thead = element "thead"
|
||||
tr = element "tr"
|
||||
|
||||
# Forms
|
||||
button = element "button"
|
||||
datalist = element "datalist"
|
||||
fieldset = element "fieldset"
|
||||
form = element "form"
|
||||
input = element "input"
|
||||
label = element "label"
|
||||
legend = element "legend"
|
||||
meter = element "meter"
|
||||
optgroup = element "optgroup"
|
||||
option = element "option"
|
||||
output = element "output"
|
||||
progress = element "progress"
|
||||
select = element "select"
|
||||
textarea = element "textarea"
|
||||
|
||||
# Interactive elements
|
||||
details = element "details"
|
||||
dialog = element "dialog"
|
||||
summary = element "summary"
|
||||
|
||||
# Web Components
|
||||
slot = element "slot"
|
||||
template = element "template"
|
277
examples/static-site-gen/platform/Html/Attributes.roc
Normal file
277
examples/static-site-gen/platform/Html/Attributes.roc
Normal file
@ -0,0 +1,277 @@
|
||||
interface Html.Attributes
|
||||
exposes [
|
||||
Attribute,
|
||||
attribute,
|
||||
accept,
|
||||
acceptCharset,
|
||||
accesskey,
|
||||
action,
|
||||
align,
|
||||
allow,
|
||||
alt,
|
||||
async,
|
||||
autocapitalize,
|
||||
autocomplete,
|
||||
autofocus,
|
||||
autoplay,
|
||||
background,
|
||||
bgcolor,
|
||||
border,
|
||||
buffered,
|
||||
capture,
|
||||
challenge,
|
||||
charset,
|
||||
checked,
|
||||
cite,
|
||||
class,
|
||||
code,
|
||||
codebase,
|
||||
color,
|
||||
cols,
|
||||
colspan,
|
||||
content,
|
||||
contenteditable,
|
||||
contextmenu,
|
||||
controls,
|
||||
coords,
|
||||
crossorigin,
|
||||
csp,
|
||||
data,
|
||||
dataAttr,
|
||||
datetime,
|
||||
decoding,
|
||||
default,
|
||||
defer,
|
||||
dir,
|
||||
dirname,
|
||||
disabled,
|
||||
download,
|
||||
draggable,
|
||||
enctype,
|
||||
enterkeyhint,
|
||||
for,
|
||||
form,
|
||||
formaction,
|
||||
formenctype,
|
||||
formmethod,
|
||||
formnovalidate,
|
||||
formtarget,
|
||||
headers,
|
||||
height,
|
||||
hidden,
|
||||
high,
|
||||
href,
|
||||
hreflang,
|
||||
httpEquiv,
|
||||
icon,
|
||||
id,
|
||||
importance,
|
||||
integrity,
|
||||
intrinsicsize,
|
||||
inputmode,
|
||||
ismap,
|
||||
itemprop,
|
||||
keytype,
|
||||
kind,
|
||||
label,
|
||||
lang,
|
||||
language,
|
||||
loading,
|
||||
list,
|
||||
loop,
|
||||
low,
|
||||
manifest,
|
||||
max,
|
||||
maxlength,
|
||||
minlength,
|
||||
media,
|
||||
method,
|
||||
min,
|
||||
multiple,
|
||||
muted,
|
||||
name,
|
||||
novalidate,
|
||||
open,
|
||||
optimum,
|
||||
pattern,
|
||||
ping,
|
||||
placeholder,
|
||||
poster,
|
||||
preload,
|
||||
radiogroup,
|
||||
readonly,
|
||||
referrerpolicy,
|
||||
rel,
|
||||
required,
|
||||
reversed,
|
||||
role,
|
||||
rows,
|
||||
rowspan,
|
||||
sandbox,
|
||||
scope,
|
||||
scoped,
|
||||
selected,
|
||||
shape,
|
||||
size,
|
||||
sizes,
|
||||
slot,
|
||||
span,
|
||||
spellcheck,
|
||||
src,
|
||||
srcdoc,
|
||||
srclang,
|
||||
srcset,
|
||||
start,
|
||||
step,
|
||||
style,
|
||||
summary,
|
||||
tabindex,
|
||||
target,
|
||||
title,
|
||||
translate,
|
||||
type,
|
||||
usemap,
|
||||
value,
|
||||
width,
|
||||
wrap,
|
||||
]
|
||||
imports []
|
||||
|
||||
Attribute : [Attribute Str Str]
|
||||
|
||||
attribute : Str -> (Str -> Attribute)
|
||||
attribute = \attrName ->
|
||||
\attrValue -> Attribute attrName attrValue
|
||||
|
||||
accept = attribute "accept"
|
||||
acceptCharset = attribute "accept-charset"
|
||||
accesskey = attribute "accesskey"
|
||||
action = attribute "action"
|
||||
align = attribute "align"
|
||||
allow = attribute "allow"
|
||||
alt = attribute "alt"
|
||||
async = attribute "async"
|
||||
autocapitalize = attribute "autocapitalize"
|
||||
autocomplete = attribute "autocomplete"
|
||||
autofocus = attribute "autofocus"
|
||||
autoplay = attribute "autoplay"
|
||||
background = attribute "background"
|
||||
bgcolor = attribute "bgcolor"
|
||||
border = attribute "border"
|
||||
buffered = attribute "buffered"
|
||||
capture = attribute "capture"
|
||||
challenge = attribute "challenge"
|
||||
charset = attribute "charset"
|
||||
checked = attribute "checked"
|
||||
cite = attribute "cite"
|
||||
class = attribute "class"
|
||||
code = attribute "code"
|
||||
codebase = attribute "codebase"
|
||||
color = attribute "color"
|
||||
cols = attribute "cols"
|
||||
colspan = attribute "colspan"
|
||||
content = attribute "content"
|
||||
contenteditable = attribute "contenteditable"
|
||||
contextmenu = attribute "contextmenu"
|
||||
controls = attribute "controls"
|
||||
coords = attribute "coords"
|
||||
crossorigin = attribute "crossorigin"
|
||||
csp = attribute "csp"
|
||||
data = attribute "data"
|
||||
dataAttr = \dataName, dataVal -> Attribute "data-\(dataName)" dataVal
|
||||
datetime = attribute "datetime"
|
||||
decoding = attribute "decoding"
|
||||
default = attribute "default"
|
||||
defer = attribute "defer"
|
||||
dir = attribute "dir"
|
||||
dirname = attribute "dirname"
|
||||
disabled = attribute "disabled"
|
||||
download = attribute "download"
|
||||
draggable = attribute "draggable"
|
||||
enctype = attribute "enctype"
|
||||
enterkeyhint = attribute "enterkeyhint"
|
||||
for = attribute "for"
|
||||
form = attribute "form"
|
||||
formaction = attribute "formaction"
|
||||
formenctype = attribute "formenctype"
|
||||
formmethod = attribute "formmethod"
|
||||
formnovalidate = attribute "formnovalidate"
|
||||
formtarget = attribute "formtarget"
|
||||
headers = attribute "headers"
|
||||
height = attribute "height"
|
||||
hidden = attribute "hidden"
|
||||
high = attribute "high"
|
||||
href = attribute "href"
|
||||
hreflang = attribute "hreflang"
|
||||
httpEquiv = attribute "http-equiv"
|
||||
icon = attribute "icon"
|
||||
id = attribute "id"
|
||||
importance = attribute "importance"
|
||||
integrity = attribute "integrity"
|
||||
intrinsicsize = attribute "intrinsicsize"
|
||||
inputmode = attribute "inputmode"
|
||||
ismap = attribute "ismap"
|
||||
itemprop = attribute "itemprop"
|
||||
keytype = attribute "keytype"
|
||||
kind = attribute "kind"
|
||||
label = attribute "label"
|
||||
lang = attribute "lang"
|
||||
language = attribute "language"
|
||||
loading = attribute "loading"
|
||||
list = attribute "list"
|
||||
loop = attribute "loop"
|
||||
low = attribute "low"
|
||||
manifest = attribute "manifest"
|
||||
max = attribute "max"
|
||||
maxlength = attribute "maxlength"
|
||||
minlength = attribute "minlength"
|
||||
media = attribute "media"
|
||||
method = attribute "method"
|
||||
min = attribute "min"
|
||||
multiple = attribute "multiple"
|
||||
muted = attribute "muted"
|
||||
name = attribute "name"
|
||||
novalidate = attribute "novalidate"
|
||||
open = attribute "open"
|
||||
optimum = attribute "optimum"
|
||||
pattern = attribute "pattern"
|
||||
ping = attribute "ping"
|
||||
placeholder = attribute "placeholder"
|
||||
poster = attribute "poster"
|
||||
preload = attribute "preload"
|
||||
radiogroup = attribute "radiogroup"
|
||||
readonly = attribute "readonly"
|
||||
referrerpolicy = attribute "referrerpolicy"
|
||||
rel = attribute "rel"
|
||||
required = attribute "required"
|
||||
reversed = attribute "reversed"
|
||||
role = attribute "role"
|
||||
rows = attribute "rows"
|
||||
rowspan = attribute "rowspan"
|
||||
sandbox = attribute "sandbox"
|
||||
scope = attribute "scope"
|
||||
scoped = attribute "scoped"
|
||||
selected = attribute "selected"
|
||||
shape = attribute "shape"
|
||||
size = attribute "size"
|
||||
sizes = attribute "sizes"
|
||||
slot = attribute "slot"
|
||||
span = attribute "span"
|
||||
spellcheck = attribute "spellcheck"
|
||||
src = attribute "src"
|
||||
srcdoc = attribute "srcdoc"
|
||||
srclang = attribute "srclang"
|
||||
srcset = attribute "srcset"
|
||||
start = attribute "start"
|
||||
step = attribute "step"
|
||||
style = attribute "style"
|
||||
summary = attribute "summary"
|
||||
tabindex = attribute "tabindex"
|
||||
target = attribute "target"
|
||||
title = attribute "title"
|
||||
translate = attribute "translate"
|
||||
type = attribute "type"
|
||||
usemap = attribute "usemap"
|
||||
value = attribute "value"
|
||||
width = attribute "width"
|
||||
wrap = attribute "wrap"
|
4
examples/static-site-gen/platform/build.rs
Normal file
4
examples/static-site-gen/platform/build.rs
Normal file
@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=dylib=app");
|
||||
println!("cargo:rustc-link-search=.");
|
||||
}
|
3
examples/static-site-gen/platform/host.c
Normal file
3
examples/static-site-gen/platform/host.c
Normal file
@ -0,0 +1,3 @@
|
||||
extern int rust_main();
|
||||
|
||||
int main() { return rust_main(); }
|
9
examples/static-site-gen/platform/main.roc
Normal file
9
examples/static-site-gen/platform/main.roc
Normal file
@ -0,0 +1,9 @@
|
||||
platform "static-site-gen"
|
||||
requires {} { transformFileContent : List U8 -> Result (List U8) Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [transformFileContentForHost]
|
||||
|
||||
transformFileContentForHost : List U8 -> Result (List U8) Str
|
||||
transformFileContentForHost = \list -> transformFileContent list
|
171
examples/static-site-gen/platform/src/lib.rs
Normal file
171
examples/static-site-gen/platform/src/lib.rs
Normal file
@ -0,0 +1,171 @@
|
||||
use core::ffi::c_void;
|
||||
use libc;
|
||||
use roc_std::{RocList, RocResult, RocStr};
|
||||
use std::env;
|
||||
use std::ffi::CStr;
|
||||
use std::fs;
|
||||
use std::os::raw::c_char;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__transformFileContentForHost_1_exposed"]
|
||||
fn roc_transformFileContentForHost(content: RocList<u8>) -> RocResult<RocList<u8>, RocStr>;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
libc::malloc(size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
libc::realloc(c_ptr, new_size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
libc::free(c_ptr)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 3 {
|
||||
eprintln!("Usage: {} path/to/input/dir path/to/output/dir", args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
match run(&args[1], &args[2]) {
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
1
|
||||
}
|
||||
Ok(()) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(
|
||||
dest: *mut c_void,
|
||||
src: *const c_void,
|
||||
bytes: usize,
|
||||
) -> *mut c_void {
|
||||
libc::memcpy(dest, src, bytes)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
||||
|
||||
fn run(input_dirname: &str, output_dirname: &str) -> Result<(), String> {
|
||||
let input_dir = PathBuf::from(input_dirname)
|
||||
.canonicalize()
|
||||
.map_err(|e| format!("{}: {}", input_dirname, e))?;
|
||||
|
||||
let output_dir = {
|
||||
let dir = PathBuf::from(output_dirname);
|
||||
if !dir.exists() {
|
||||
fs::create_dir(&dir).unwrap();
|
||||
}
|
||||
dir.canonicalize()
|
||||
.map_err(|e| format!("{}: {}", output_dirname, e))?
|
||||
};
|
||||
|
||||
let mut input_files: Vec<PathBuf> = vec![];
|
||||
find_files(&input_dir, &mut input_files)
|
||||
.map_err(|e| format!("Error finding input files: {}", e))?;
|
||||
|
||||
println!("Processing {} input files...", input_files.len());
|
||||
|
||||
let mut had_errors = false;
|
||||
|
||||
// TODO: process the files asynchronously
|
||||
for input_file in input_files {
|
||||
match process_file(&input_dir, &output_dir, &input_file) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
had_errors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if had_errors {
|
||||
Err("Could not process all files".into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Result<(), String> {
|
||||
let rust_content = match fs::read(input_file) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) => {
|
||||
return Err(format!(
|
||||
"Error reading {}: {}",
|
||||
input_file.to_str().unwrap_or("an input file"),
|
||||
e
|
||||
));
|
||||
}
|
||||
};
|
||||
let roc_content = RocList::from_iter(rust_content);
|
||||
let roc_result = unsafe { roc_transformFileContentForHost(roc_content) };
|
||||
match Result::from(roc_result) {
|
||||
Ok(roc_output_bytes) => {
|
||||
let input_relpath = input_file
|
||||
.strip_prefix(input_dir)
|
||||
.map_err(|e| e.to_string())?
|
||||
.to_path_buf();
|
||||
|
||||
let mut output_relpath = input_relpath.clone();
|
||||
output_relpath.set_extension("html");
|
||||
|
||||
let output_file = output_dir.join(&output_relpath);
|
||||
let rust_output_bytes = Vec::from_iter(roc_output_bytes.into_iter());
|
||||
fs::write(&output_file, &rust_output_bytes).map_err(|e| format!("{}", e))?;
|
||||
|
||||
println!(
|
||||
"{} -> {}",
|
||||
input_relpath.display(),
|
||||
output_relpath.display()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Err(roc_error_str) => Err(format!(
|
||||
"Error transforming {}: {}",
|
||||
input_file.to_str().unwrap_or("an input file"),
|
||||
roc_error_str.as_str()
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_files(dir: &Path, file_paths: &mut Vec<PathBuf>) -> std::io::Result<()> {
|
||||
for entry in fs::read_dir(dir)? {
|
||||
let pathbuf = entry?.path();
|
||||
if pathbuf.is_dir() {
|
||||
find_files(&pathbuf, file_paths)?;
|
||||
} else {
|
||||
file_paths.push(pathbuf);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
3
examples/static-site-gen/platform/src/main.rs
Normal file
3
examples/static-site-gen/platform/src/main.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
std::process::exit(host::rust_main());
|
||||
}
|
26
examples/static-site-gen/static-site.roc
Normal file
26
examples/static-site-gen/static-site.roc
Normal file
@ -0,0 +1,26 @@
|
||||
app "static-site"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Html.{ html, head, body, div, text }]
|
||||
provides [transformFileContent] to pf
|
||||
|
||||
transformFileContent : List U8 -> Result (List U8) Str
|
||||
transformFileContent = \content ->
|
||||
when Str.fromUtf8 content is
|
||||
Err _ -> Err "Invalid UTF-8"
|
||||
Ok contentStr ->
|
||||
contentStr
|
||||
|> view
|
||||
|> Html.render
|
||||
|> Str.toUtf8
|
||||
|> Ok
|
||||
|
||||
view : Str -> Html.Node
|
||||
view = \content ->
|
||||
html [] [
|
||||
head [] [],
|
||||
body [] [
|
||||
div [] [
|
||||
text content,
|
||||
],
|
||||
],
|
||||
]
|
Loading…
Reference in New Issue
Block a user