,
80 | }
81 |
82 | fn walk_dir(base_path: P) -> io::Result>
83 | where
84 | P: AsRef,
85 | {
86 | fn walk_nested(base_path: &Path, path: &Path) -> io::Result> {
87 | let dir = fs::read_dir(path)?;
88 | let mut static_files = Vec::new();
89 | for entry in dir {
90 | let entry = entry?;
91 | let file_type = entry.file_type()?;
92 | if file_type.is_dir() {
93 | static_files.extend(walk_nested(base_path, &entry.path())?.into_iter());
94 | } else {
95 | let entry_path = entry.path();
96 | let entry_path = entry_path
97 | .strip_prefix(base_path)
98 | .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
99 | let mime = mime_guess::from_path(entry_path)
100 | .first()
101 | .unwrap_or(mime::TEXT_PLAIN);
102 |
103 | let content = fs::read(entry.path())?;
104 |
105 | static_files.push(StaticFile {
106 | mime,
107 | path: entry_path
108 | .to_str()
109 | .ok_or_else(|| {
110 | io::Error::new(
111 | io::ErrorKind::Other,
112 | "unable to convert path to UTF-8 string",
113 | )
114 | })?
115 | .to_string(),
116 | content,
117 | });
118 | }
119 | }
120 |
121 | Ok(static_files)
122 | }
123 |
124 | walk_nested(base_path.as_ref(), base_path.as_ref())
125 | }
126 |
--------------------------------------------------------------------------------
/submillisecond_macros/src/router/item_route.rs:
--------------------------------------------------------------------------------
1 | use proc_macro2::TokenStream;
2 | use quote::{ToTokens, TokenStreamExt};
3 | use syn::parse::{Parse, ParseStream};
4 | use syn::spanned::Spanned;
5 | use syn::{braced, token, Expr, LitStr, Path, Token};
6 |
7 | use super::item_with_middleware::ItemWithMiddleware;
8 | use super::method::Method;
9 | use super::with;
10 | use crate::hquote;
11 | use crate::router::Router;
12 |
13 | /// `"/abc" => sub_router`
14 | /// `GET "/abc" => handler`
15 | /// `GET "/abc" if guard => handler`
16 | /// `GET "/abc" use middleware => handler`
17 | /// `GET "/abc" if guard use middleware => handler`
18 | #[derive(Clone, Debug)]
19 | pub struct ItemRoute {
20 | pub method: Option,
21 | pub path: LitStr,
22 | pub guard: Option,
23 | pub middleware: Option,
24 | pub fat_arrow_token: Token![=>],
25 | pub handler: ItemHandler,
26 | }
27 |
28 | impl Parse for ItemRoute {
29 | fn parse(input: ParseStream) -> syn::Result {
30 | let item_route = ItemRoute {
31 | method: if Method::peek(input) {
32 | Some(input.parse()?)
33 | } else {
34 | None
35 | },
36 | path: input.parse()?,
37 | guard: if input.peek(Token![if]) {
38 | Some(input.parse()?)
39 | } else {
40 | None
41 | },
42 | middleware: if input.peek(with) {
43 | Some(input.parse()?)
44 | } else {
45 | None
46 | },
47 | fat_arrow_token: input.parse()?,
48 | handler: input.parse()?,
49 | };
50 |
51 | if let Some(method) = item_route.method {
52 | if matches!(item_route.handler, ItemHandler::SubRouter(_)) {
53 | return Err(syn::Error::new(
54 | method.span(),
55 | "method prefix cannot be used with sub routers",
56 | ));
57 | }
58 | }
59 |
60 | let path = item_route.path.value();
61 | if let Some(pos) = path.find('*') {
62 | if pos < path.len() - 1 {
63 | return Err(syn::Error::new(
64 | item_route.path.span(),
65 | "wildcards must be placed at the end of the path",
66 | ));
67 | }
68 |
69 | if item_route.method.is_none() {
70 | return Err(syn::Error::new(
71 | item_route.path.span(),
72 | "wildcards cannot be used with sub routers - try adding a HTTP method",
73 | ));
74 | }
75 | }
76 |
77 | Ok(item_route)
78 | }
79 | }
80 |
81 | #[derive(Clone, Debug)]
82 | pub struct ItemGuard {
83 | pub if_token: Token![if],
84 | pub guard: Box,
85 | }
86 |
87 | impl Parse for ItemGuard {
88 | fn parse(input: ParseStream) -> syn::Result {
89 | Ok(ItemGuard {
90 | if_token: input.parse()?,
91 | guard: Box::new(input.call(Expr::parse_without_eager_brace)?),
92 | })
93 | }
94 | }
95 |
96 | impl ToTokens for ItemGuard {
97 | fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
98 | tokens.append_all(expand_guard_struct(&self.guard));
99 | }
100 | }
101 |
102 | fn expand_guard_struct(guard: &syn::Expr) -> TokenStream {
103 | match guard {
104 | Expr::Binary(expr_binary) => {
105 | let left = expand_guard_struct(&expr_binary.left);
106 | let op = &expr_binary.op;
107 | let right = expand_guard_struct(&expr_binary.right);
108 |
109 | hquote! { #left #op #right }
110 | }
111 | Expr::Paren(expr_paren) => {
112 | let expr = expand_guard_struct(&expr_paren.expr);
113 | hquote! { (#expr) }
114 | }
115 | expr => hquote! { ::submillisecond::Guard::check(expr, &req) },
116 | }
117 | }
118 |
119 | #[derive(Clone, Debug)]
120 | pub enum ItemHandler {
121 | Expr(Box),
122 | SubRouter(Router),
123 | }
124 |
125 | impl Parse for ItemHandler {
126 | fn parse(input: ParseStream) -> syn::Result {
127 | if input.peek(token::Brace) {
128 | let content;
129 | braced!(content in input);
130 | return Ok(ItemHandler::SubRouter(content.parse()?));
131 | }
132 |
133 | let fork = input.fork();
134 | let _: Path = fork.parse()?;
135 | Ok(ItemHandler::Expr(input.parse()?))
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/reader.rs:
--------------------------------------------------------------------------------
1 | //! Uri reader using a cursor.
2 | //!
3 | //! The [`router!`](crate::router) macro uses this internally for routing.
4 |
5 | /// A uri string and cursor reader.
6 | #[derive(Clone, Debug, Default)]
7 | pub struct UriReader {
8 | /// Request uri.
9 | pub uri: String,
10 | /// Request uri cursor.
11 | pub cursor: usize,
12 | }
13 |
14 | impl UriReader {
15 | /// Creates a new [`UriReader`] with the cursor set to `0`.
16 | pub fn new(uri: String) -> UriReader {
17 | UriReader { uri, cursor: 0 }
18 | }
19 |
20 | /// Returns the next `len` characters from the uri, without modifying the
21 | /// cursor position.
22 | pub fn peek(&self, len: usize) -> &str {
23 | let read_attempt = self.cursor + len;
24 | if self.uri.len() >= read_attempt {
25 | return &self.uri[self.cursor..read_attempt];
26 | }
27 | ""
28 | }
29 |
30 | /// Returns a bool indicating whether the reader has reached the end,
31 | /// disregarding any trailing slash.
32 | pub fn is_dangling_slash(&self) -> bool {
33 | self.uri.len() == self.cursor || &self.uri[self.cursor..self.cursor + 1] == "/"
34 | }
35 |
36 | /// Returns a bool indicating whether the reader has reached the end,
37 | /// disregarding any trailing slash.
38 | pub fn is_dangling_terminal_slash(&self) -> bool {
39 | self.uri.len() == self.cursor
40 | || (&self.uri[self.cursor..self.cursor + 1] == "/" && self.uri.len() - self.cursor == 1)
41 | }
42 |
43 | /// Move the cursor forward based on `len`.
44 | pub fn read(&mut self, len: usize) {
45 | self.cursor += len;
46 | }
47 |
48 | /// Attempt to read `s` from the current cursor position, modifying the
49 | /// cursor if a match was found.
50 | ///
51 | /// `true` is returned if `s` matched the uri and the cursor was updated.
52 | pub fn read_matching(&mut self, s: &str) -> bool {
53 | let read_to = self.cursor + s.len();
54 | if read_to > self.uri.len() {
55 | return false;
56 | }
57 |
58 | if &self.uri[self.cursor..read_to] == s {
59 | self.cursor = read_to;
60 | return true;
61 | }
62 |
63 | false
64 | }
65 |
66 | /// Reads a single `/` character, modifying the cursor and returning whether
67 | /// there was a match.
68 | pub fn ensure_next_slash(&mut self) -> bool {
69 | self.read_matching("/")
70 | }
71 |
72 | /// Reset the cursor to the start of the uri.
73 | pub fn reset(&mut self) {
74 | self.cursor = 0;
75 | }
76 |
77 | /// Check if the cursor has reached the end of the uri, optionally allowing
78 | /// for a trailing slash.
79 | pub fn is_empty(&self, allow_trailing_slash: bool) -> bool {
80 | if allow_trailing_slash {
81 | self.uri.len() <= self.cursor || &self.uri[self.cursor..] == "/"
82 | } else {
83 | self.uri.len() <= self.cursor
84 | }
85 | }
86 |
87 | /// Read a param until the next `/` or end of uri.
88 | pub fn read_param(&mut self) -> Option<&str> {
89 | let initial_cursor = self.cursor;
90 | while !self.is_empty(false) {
91 | if self.peek(1) != "/" {
92 | self.read(1);
93 | } else {
94 | break;
95 | }
96 | }
97 | // if nothing was found, return none
98 | if initial_cursor == self.cursor {
99 | return None;
100 | }
101 | // read the param
102 | Some(&self.uri[initial_cursor..self.cursor])
103 | }
104 |
105 | /// Check if the uri ends with `suffix`.
106 | pub fn ends_with(&self, suffix: &str) -> bool {
107 | if self.cursor >= self.uri.len() {
108 | return false;
109 | }
110 | let end = &self.uri[self.cursor..];
111 | end == suffix
112 | }
113 |
114 | /// Returns the remainder of the uri from the current cursor position.
115 | pub fn read_to_end(&self) -> &str {
116 | &self.uri[self.cursor..]
117 | }
118 | }
119 |
120 | #[cfg(test)]
121 | mod tests {
122 | use super::UriReader;
123 |
124 | #[test]
125 | fn peek_empty_string() {
126 | let reader = UriReader::new("".to_string());
127 | assert_eq!(reader.peek(5), "");
128 | }
129 |
130 | #[test]
131 | fn peek_path() {
132 | let mut reader = UriReader::new("/alive".to_string());
133 | assert_eq!(reader.peek(3), "/al");
134 | reader.read(3);
135 | assert_eq!(reader.peek(3), "ive");
136 | reader.read(3);
137 | assert_eq!(reader.peek(3), "");
138 | reader.read(3);
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/macros.rs:
--------------------------------------------------------------------------------
1 | macro_rules! define_rejection {
2 | (
3 | #[status = $status:ident]
4 | #[body = $body:expr]
5 | $(#[$m:meta])*
6 | pub struct $name:ident;
7 | ) => {
8 | $(#[$m])*
9 | #[derive(Debug)]
10 | #[non_exhaustive]
11 | pub struct $name;
12 |
13 | impl $crate::response::IntoResponse for $name {
14 | fn into_response(self) -> $crate::Response {
15 | (http::StatusCode::$status, $body).into_response()
16 | }
17 | }
18 |
19 | impl std::fmt::Display for $name {
20 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 | write!(f, "{}", $body)
22 | }
23 | }
24 |
25 | impl std::error::Error for $name {}
26 |
27 | impl Default for $name {
28 | fn default() -> Self {
29 | Self
30 | }
31 | }
32 | };
33 |
34 | (
35 | #[status = $status:ident]
36 | #[body = $body:expr]
37 | $(#[$m:meta])*
38 | pub struct $name:ident (Error);
39 | ) => {
40 | $(#[$m])*
41 | #[derive(Debug)]
42 | pub struct $name(pub(crate) crate::Error);
43 |
44 | impl $name {
45 | pub(crate) fn from_err(err: E) -> Self
46 | where
47 | E: Into,
48 | {
49 | Self(crate::Error::new(err))
50 | }
51 | }
52 |
53 | impl crate::response::IntoResponse for $name {
54 | fn into_response(self) -> $crate::Response {
55 | (
56 | http::StatusCode::$status,
57 | format!(concat!($body, ": {}"), self.0),
58 | ).into_response()
59 | }
60 | }
61 |
62 | impl std::fmt::Display for $name {
63 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 | write!(f, "{}", $body)
65 | }
66 | }
67 |
68 | impl std::error::Error for $name {
69 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
70 | Some(&self.0)
71 | }
72 | }
73 | };
74 | }
75 |
76 | macro_rules! composite_rejection {
77 | (
78 | $(#[$m:meta])*
79 | pub enum $name:ident {
80 | $($variant:ident),+
81 | $(,)?
82 | }
83 | ) => {
84 | $(#[$m])*
85 | #[derive(Debug)]
86 | #[non_exhaustive]
87 | pub enum $name {
88 | $(
89 | #[allow(missing_docs)]
90 | $variant($variant)
91 | ),+
92 | }
93 |
94 | impl $crate::response::IntoResponse for $name {
95 | fn into_response(self) -> $crate::Response {
96 | match self {
97 | $(
98 | Self::$variant(inner) => inner.into_response(),
99 | )+
100 | }
101 | }
102 | }
103 |
104 | $(
105 | impl From<$variant> for $name {
106 | fn from(inner: $variant) -> Self {
107 | Self::$variant(inner)
108 | }
109 | }
110 | )+
111 |
112 | impl std::fmt::Display for $name {
113 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 | match self {
115 | $(
116 | Self::$variant(inner) => write!(f, "{}", inner),
117 | )+
118 | }
119 | }
120 | }
121 |
122 | impl std::error::Error for $name {
123 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
124 | match self {
125 | $(
126 | Self::$variant(inner) => Some(inner),
127 | )+
128 | }
129 | }
130 | }
131 | };
132 | }
133 |
134 | macro_rules! all_the_tuples {
135 | ($name:ident) => {
136 | $name!(T1);
137 | $name!(T1, T2);
138 | $name!(T1, T2, T3);
139 | $name!(T1, T2, T3, T4);
140 | $name!(T1, T2, T3, T4, T5);
141 | $name!(T1, T2, T3, T4, T5, T6);
142 | $name!(T1, T2, T3, T4, T5, T6, T7);
143 | $name!(T1, T2, T3, T4, T5, T6, T7, T8);
144 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
145 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
146 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
147 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
148 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
149 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
150 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
151 | $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
152 | };
153 | ($name:ident, numbered) => {
154 | $name!(1, T1);
155 | $name!(2, T1, T2);
156 | $name!(3, T1, T2, T3);
157 | $name!(4, T1, T2, T3, T4);
158 | $name!(5, T1, T2, T3, T4, T5);
159 | $name!(6, T1, T2, T3, T4, T5, T6);
160 | $name!(7, T1, T2, T3, T4, T5, T6, T7);
161 | $name!(8, T1, T2, T3, T4, T5, T6, T7, T8);
162 | $name!(9, T1, T2, T3, T4, T5, T6, T7, T8, T9);
163 | $name!(10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
164 | $name!(11, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
165 | $name!(12, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
166 | $name!(13, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
167 | $name!(14, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
168 | $name!(15, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
169 | $name!(16, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
170 | };
171 | }
172 |
--------------------------------------------------------------------------------
/src/handler.rs:
--------------------------------------------------------------------------------
1 | use lunatic::function::reference::Fn as FnPtr;
2 | use lunatic::function::FuncRef;
3 | use serde::de::DeserializeOwned;
4 | use serde::Serialize;
5 |
6 | use crate::extract::{FromOwnedRequest, FromRequest};
7 | use crate::response::IntoResponse;
8 | use crate::{RequestContext, Response};
9 |
10 | /// Implemented for process-safe [`Handlers`](Handler).
11 | ///
12 | /// Submillisecond handles every request in a separate lunatic process, and
13 | /// lunatic's processes are sandboxed. This means that no memory is shared
14 | /// between the request handler and the rest of the app. This introduces an
15 | /// additional limitation on what can be a [`Handler`].
16 | ///
17 | /// Two kinds of types are safe to be used as handlers:
18 | /// - Static functions
19 | /// - Serializable and clonable objects
20 | ///
21 | /// ### Static functions
22 | ///
23 | /// This type is obvious. Non-capturing functions are generated during compile
24 | /// time and are shared between all processes, so they can be easily used as
25 | /// handlers. In fact, the [`router!`](crate::router) macro will in the end just
26 | /// generate a function that will be used as a handler and invoke other handlers
27 | /// depending on the request values.
28 | ///
29 | /// ### Serializable and clonable objects
30 | ///
31 | /// Everything else needs to be passed somehow to the memory of the handler
32 | /// process. This means that we need to clone the value for every incoming
33 | /// request, serialize it and send it to the process handling the request.
34 | pub trait ProcessSafeHandler {
35 | /// A handler is only safe if it can be cloned and safely sent between
36 | /// processes.
37 | type SafeHandler: Handler + Clone + Serialize + DeserializeOwned;
38 |
39 | /// Turn type into a safe handler.
40 | fn safe_handler(self) -> Self::SafeHandler;
41 | }
42 |
43 | /// Marker type for functions that satisfy [`ProcessSafeHandler`].
44 | pub struct Function;
45 | /// Marker type for objects that satisfy [`ProcessSafeHandler`].
46 | pub struct Object;
47 |
48 | impl ProcessSafeHandler for T
49 | where
50 | T: FnPtr + Copy,
51 | FuncRef: Handler,
52 | {
53 | type SafeHandler = FuncRef;
54 |
55 | fn safe_handler(self) -> Self::SafeHandler {
56 | FuncRef::new(self)
57 | }
58 | }
59 |
60 | impl ProcessSafeHandler