Skip to main content

trillium_example/
main.rs

1use askama::Template;
2use futures_lite::prelude::*;
3use std::time::Duration;
4use trillium::{Conn, Handler};
5use trillium_askama::AskamaConnExt;
6use trillium_caching_headers::{
7    CacheControlDirective::{MaxAge, Public},
8    CachingHeadersExt,
9};
10use trillium_conn_id::log_formatter::conn_id;
11use trillium_logger::apache_common;
12use trillium_proxy::Proxy;
13use trillium_router::{Router, RouterConnExt};
14use trillium_rustls::RustlsConfig;
15use trillium_sessions::{MemoryStore, SessionConnExt};
16use trillium_smol::ClientConfig;
17use trillium_static_compiled::static_compiled;
18use trillium_websockets::{Message, WebSocket, WebSocketConn};
19
20#[derive(Template)]
21#[template(path = "hello.html")]
22struct HelloTemplate<'a> {
23    name: &'a str,
24}
25
26async fn request_count(conn: Conn) -> Conn {
27    let count = conn.session().get::<usize>("count").unwrap_or_default();
28    conn.with_response_header("request-count", count.to_string())
29        .with_session("count", count + 1)
30}
31
32async fn with_cache_control(conn: Conn) -> Conn {
33    conn.with_cache_control([MaxAge(Duration::from_secs(604800)), Public])
34        .with_vary([trillium::KnownHeaderName::UserAgent])
35}
36
37fn app() -> impl Handler {
38    (
39        with_cache_control,
40        trillium_logger::logger().with_formatter(apache_common(conn_id, "-")),
41        trillium_compression::compression(),
42        trillium_conn_id::conn_id(),
43        trillium_method_override::method_override(),
44        trillium_head::head(),
45        trillium_caching_headers::caching_headers(),
46        trillium_cookies::cookies(),
47        trillium_sessions::sessions(MemoryStore::new(), b"01234567890123456789012345678901123"),
48        request_count,
49        router(),
50        static_compiled!("$CARGO_MANIFEST_DIR/public").with_index_file("index.html"),
51    )
52}
53
54fn router() -> impl Handler {
55    Router::new()
56        .get("/hello", "hi")
57        .post("/", |mut conn: Conn| async move {
58            let body = conn.request_body_string().await.unwrap();
59            conn.ok(format!("request body: {body}"))
60        })
61        .get("/template/:name", |conn: Conn| async move {
62            if let Some(name) = conn.param("name").map(String::from) {
63                conn.render(HelloTemplate { name: &name })
64            } else {
65                conn
66            }
67        })
68        .get("/hello/:planet", |conn: Conn| async move {
69            if let Some(planet) = conn.param("planet") {
70                let response = format!("hello, {planet}");
71                conn.ok(response)
72            } else {
73                conn
74            }
75        })
76        .get(
77            "/ws",
78            WebSocket::new(|mut ws: WebSocketConn| async move {
79                while let Some(Ok(Message::Text(input))) = ws.next().await {
80                    log::info!("received message {:?}", &input);
81                    let output: String = input.chars().rev().collect();
82                    let _ = ws.send_string(format!("{} | {}", &input, &output)).await;
83                }
84            }),
85        )
86        .get(
87            "/httpbin/*",
88            Proxy::new(
89                RustlsConfig::default().with_tcp_config(ClientConfig::default()),
90                "https://httpbin.org",
91            ),
92        )
93}
94
95fn main() {
96    env_logger::init();
97    trillium_smol::run(app());
98}
99
100#[cfg(test)]
101mod test {
102    use trillium_testing::prelude::*;
103
104    use super::app;
105
106    #[test]
107    fn test_index() {
108        let app = app();
109        let mut conn = get("/").on(&app);
110        assert_ok!(&conn);
111        assert_body_contains!(&mut conn, "<h1>Welcome to trillium!</h1>");
112    }
113
114    #[test]
115    fn test_hello_hi() {
116        let app = app();
117        assert_ok!(get("/hello").on(&app), "hi");
118    }
119
120    #[test]
121    fn test_post_index() {
122        let app = app();
123        assert_ok!(
124            post("/").with_request_body("hey").on(&app),
125            "request body: hey"
126        );
127    }
128
129    #[test]
130    fn test_askama_templating() {
131        let app = app();
132        assert_body_contains!(
133            get("/template/trillium").on(&app),
134            "<h1>hi there, trillium</h1>"
135        );
136
137        assert_body_contains!(
138            get("/template/dear-reader").on(&app),
139            "<h1>hi there, dear-reader</h1>"
140        );
141    }
142}