Files
soda/src/lib.rs
2025-02-13 15:08:59 -05:00

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));
}
}
}