Skip to main content

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}