trillium_static/lib.rs
1#![forbid(unsafe_code)]
2#![deny(
3 clippy::dbg_macro,
4 missing_copy_implementations,
5 rustdoc::missing_crate_level_docs,
6 missing_debug_implementations,
7 missing_docs,
8 nonstandard_style,
9 unused_qualifications
10)]
11
12//! Serves static file assets from the file system.
13//!
14//! ```
15//! # #[cfg(not(unix))] fn main() {}
16//! # #[cfg(unix)] fn main() {
17//! use trillium_static::{StaticFileHandler, crate_relative_path};
18//! use trillium_testing::TestServer;
19//!
20//! # trillium_testing::block_on(async {
21//! let handler = StaticFileHandler::new(crate_relative_path!("examples/files"))
22//! .with_index_file("index.html");
23//!
24//! // given the following directory layout
25//! //
26//! // examples/files
27//! // ├── index.html
28//! // ├── subdir
29//! // │ └── index.html
30//! // └── subdir_with_no_index
31//! // └── plaintext.txt
32//!
33//! let app = TestServer::new(handler).await;
34//!
35//! app.get("/")
36//! .await
37//! .assert_ok()
38//! .assert_body("<h1>hello world</h1>\n")
39//! .assert_header("content-type", "text/html; charset=utf-8");
40//!
41//! app.get("/file_that_does_not_exist.txt")
42//! .await
43//! .assert_status(404);
44//!
45//! app.get("/index.html").await.assert_ok();
46//!
47//! app.get("/subdir/index.html")
48//! .await
49//! .assert_ok()
50//! .assert_body("subdir index.html\n");
51//!
52//! app.get("/subdir")
53//! .await
54//! .assert_ok()
55//! .assert_body("subdir index.html\n");
56//!
57//! app.get("/subdir_with_no_index").await.assert_status(404);
58//!
59//! app.get("/subdir_with_no_index/plaintext.txt")
60//! .await
61//! .assert_ok()
62//! .assert_body("plaintext file\n")
63//! .assert_header("content-type", "text/plain; charset=utf-8");
64//!
65//! // with a different index file
66//! let plaintext_index = StaticFileHandler::new(crate_relative_path!("examples/files"))
67//! .with_index_file("plaintext.txt");
68//!
69//! let app2 = TestServer::new(plaintext_index).await;
70//!
71//! app2.get("/").await.assert_status(404);
72//! app2.get("/subdir").await.assert_status(404);
73//!
74//! app2.get("/subdir_with_no_index")
75//! .await
76//! .assert_ok()
77//! .assert_body("plaintext file\n")
78//! .assert_header("content-type", "text/plain; charset=utf-8");
79//! # });
80//! # }
81//! ```
82//!
83//!
84//! ## ❗IMPORTANT❗
85//!
86//! this crate has three features currently: `smol`, `async-std`, and
87//! `tokio`.
88//!
89//! You **must** enable one of these in order to use the crate. This
90//! is intended to be a temporary situation, and eventually you will not
91//! need to specify the runtime through feature flags.
92//!
93//! ## stability note
94//!
95//! Please note that this crate is fairly incomplete, while functional. It
96//! does not include any notion of range requests or cache headers. It
97//! serves all files from disk every time, with no in-memory caching.
98
99#[cfg(test)]
100#[doc = include_str!("../README.md")]
101mod readme {}
102
103mod fs_shims;
104mod handler;
105mod options;
106mod static_conn_ext;
107
108pub use handler::StaticFileHandler;
109pub use relative_path;
110pub use static_conn_ext::StaticConnExt;
111
112/// a convenient helper macro to build a str relative to the crate root
113#[macro_export]
114macro_rules! crate_relative_path {
115 ($path:literal) => {
116 $crate::relative_path::RelativePath::new($path).to_logical_path(env!("CARGO_MANIFEST_DIR"))
117 };
118}
119
120/// convenience alias for [`StaticFileHandler::new`]
121pub fn files(fs_root: impl AsRef<std::path::Path>) -> StaticFileHandler {
122 StaticFileHandler::new(fs_root)
123}