mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
examples: first draft of static site generator example
This commit is contained in:
parent
c1a38bc400
commit
7d6f0b3806
2
examples/static-site-gen/.gitignore
vendored
Normal file
2
examples/static-site-gen/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/output
|
||||
/app
|
26
examples/static-site-gen/app.roc
Normal file
26
examples/static-site-gen/app.roc
Normal file
@ -0,0 +1,26 @@
|
||||
app "app"
|
||||
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,
|
||||
],
|
||||
],
|
||||
]
|
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]
|
6
examples/static-site-gen/platform/build.rs
Normal file
6
examples/static-site-gen/platform/build.rs
Normal file
@ -0,0 +1,6 @@
|
||||
// If you open this platform in an IDE, with a rust-analyzer plugin,
|
||||
// it needs to be able to see a custom Cargo build script.
|
||||
// Otherwise it complains that Cargo will not link the host to the app.
|
||||
// (The IDE has no way of knowing about the Roc compiler, which is our real "custom build script"!)
|
||||
// This dummy build script is enough to keep everybody happy.
|
||||
fn main() {}
|
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 = transformFileContent
|
125
examples/static-site-gen/platform/src/lib.rs
Normal file
125
examples/static-site-gen/platform/src/lib.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use core::ffi::c_void;
|
||||
use libc;
|
||||
use roc_std::{RocList, RocResult, RocStr};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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 = PathBuf::from(output_dirname)
|
||||
.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) };
|
||||
dbg!(&roc_result); // TODO: always seem to be Err(""), I'm not sure why.
|
||||
match Result::from(roc_result) {
|
||||
Ok(roc_output_bytes) => {
|
||||
let relpath = input_file
|
||||
.strip_prefix(input_dir)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let output_file = output_dir.join(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))
|
||||
}
|
||||
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());
|
||||
}
|
Loading…
Reference in New Issue
Block a user