98 lines
4.0 KiB
Rust
98 lines
4.0 KiB
Rust
/* Soda
|
|
by Tyler Clarke
|
|
|
|
Soda is a convenient frontend framework... written in backend rust.
|
|
It allows you to turn a complicated webpage design into a single server-side executable that runs on any Linux system.
|
|
A core element of soda's philosophy is the idea of staying fully static: this allows the optimizing compiler to reduce potentially thousands of lines
|
|
of frontend code to a few dozen inlined socket writes, which is *enormously* fast.
|
|
Soda provides a set of powerful dynamic features atop the static rendering ones as well.
|
|
|
|
Soda is built atop Hyper, the fastest correct HTTP server implementation in rust.
|
|
*/
|
|
|
|
|
|
pub mod compose;
|
|
pub mod output;
|
|
pub mod html;
|
|
pub mod tuples;
|
|
pub mod router;
|
|
pub mod prelude;
|
|
pub mod server;
|
|
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::prelude::*;
|
|
|
|
#[test]
|
|
fn render_composer() { // simplest example: renders a composer with DummyOutput
|
|
let page = PageBase (
|
|
"Test Webpage",
|
|
Tag::Paragraph("Hello, World!")
|
|
);
|
|
let mut output = output::DummyOutput::new();
|
|
page.render(&mut output, &mut ());
|
|
assert_eq!(output.spool(), r#"<!DOCTYPE html><html><head><meta charset="utf-8"/><title>Test Webpage</title></head><body><p>Hello, World!</p></body></html>"#);
|
|
}
|
|
|
|
#[test]
|
|
fn routing_test() {
|
|
fn template<State>(content : impl Compose<State>) -> impl Compose<State> {
|
|
PageBase (
|
|
"My Webpage",
|
|
Tag::Div((
|
|
Tag::Heading1("My Test Website"),
|
|
content
|
|
))
|
|
)
|
|
}
|
|
let page1 = template("this is the first page");
|
|
let page2 = template("this is the second page");
|
|
let page3 = template("this is the third page");
|
|
let app = page1.route_at("page1") // route GET /page1
|
|
.or(page2.route_at("page2")) // route GET /page2
|
|
.or(router::route("page_num").then(page3.route_at("3"))); // route GET /page_num/3
|
|
let mut output = output::DummyOutput::new();
|
|
app.get("https://web.app/page1").unwrap().render(&mut output, &mut ());
|
|
assert_eq!(output.spool(), r#"<!DOCTYPE html><html><head><meta charset="utf-8"/><title>My Webpage</title></head><body><div><h1>My Test Website</h1>this is the first page</div></body></html>"#);
|
|
output = output::DummyOutput::new();
|
|
app.get("https://web.app/page2").unwrap().render(&mut output, &mut ());
|
|
assert_eq!(output.spool(), r#"<!DOCTYPE html><html><head><meta charset="utf-8"/><title>My Webpage</title></head><body><div><h1>My Test Website</h1>this is the second page</div></body></html>"#);
|
|
output = output::DummyOutput::new();
|
|
app.get("https://web.app/page_num/3").unwrap().render(&mut output, &mut ());
|
|
assert_eq!(output.spool(), r#"<!DOCTYPE html><html><head><meta charset="utf-8"/><title>My Webpage</title></head><body><div><h1>My Test Website</h1>this is the third page</div></body></html>"#);
|
|
}
|
|
|
|
#[test]
|
|
fn closure_test() {
|
|
// you can use embedded blocks to procedurally generate outputs
|
|
let app = Tag::Div(("Hello, ", {
|
|
"World!"
|
|
}));
|
|
let mut output = output::DummyOutput::new();
|
|
app.render(&mut output, &mut ());
|
|
assert_eq!(output.spool(), r#"<div>Hello, World!</div>"#);
|
|
}
|
|
|
|
#[test]
|
|
fn dynamic_globalstate_test() {
|
|
// composers must always be immutable. however, you can produce dynamic mutable behavior with State
|
|
// it's literally just a generic of Component
|
|
// you can pass pretty much whatever you want in there.
|
|
let app_with_state = (
|
|
"this page has been visited ",
|
|
|state : &mut u32 | {
|
|
*state += 1;
|
|
state.to_string()
|
|
},
|
|
" times."
|
|
);
|
|
let mut state : u32 = 0;
|
|
for x in 0..5 {
|
|
let mut output = output::DummyOutput::new();
|
|
app_with_state.render(&mut output, &mut state);
|
|
assert_eq!(output.spool(), format!("this page has been visited {} times.", x + 1));
|
|
}
|
|
}
|
|
}
|