examples: first draft of static site generator example

This commit is contained in:
Brian Carroll 2022-08-28 14:33:03 +01:00
parent c1a38bc400
commit 7d6f0b3806
No known key found for this signature in database
GPG Key ID: 5C7B2EC4101703C0
10 changed files with 237 additions and 0 deletions

2
examples/static-site-gen/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/output
/app

View 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,
],
],
]

View 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.

View 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"

View 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]

View 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() {}

View File

@ -0,0 +1,3 @@
extern int rust_main();
int main() { return rust_main(); }

View 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

View 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(())
}

View File

@ -0,0 +1,3 @@
fn main() {
std::process::exit(host::rust_main());
}