diff --git a/src/handling.rs b/src/handling.rs index f1e2639..ba72283 100644 --- a/src/handling.rs +++ b/src/handling.rs @@ -2,10 +2,6 @@ //! //! The main type is the [`Handler`], which wraps a more specific type of handler and //! manages delegating responses to it. -//! -//! For most purposes, you should never have to manually create any of these structs -//! yourself, though it may be useful to look at the implementations of [`From`] on -//! [`Handler`], as these are the things that can be used as handlers for routes. use anyhow::Result; use std::{ @@ -21,24 +17,63 @@ use crate::{Document, types::{Body, Response, Request}}; /// A struct representing something capable of handling a request. /// -/// In the future, this may have multiple varieties, but at the minute, it just wraps an -/// [`Fn`](std::ops::Fn). +/// A crucial part of the documentation for this is the implementations of [`From`], as +/// this is what can be passed to [`Server::add_route()`](crate::Server::add_route()) in +/// order to create a new route. /// -/// The most useful part of the documentation for this is the implementations of [`From`] -/// on it, as this is what can be passed to -/// [`Server::add_route()`](crate::Server::add_route()) in order to create a new route. -/// Each implementation has bespoke docs that describe how the type is used, and what -/// response is produced. +/// Detailed descriptions on each variant also describe how each kind of handler works, +/// and how they can be created pub enum Handler { /// A handler that responds to a request by delegating to an [`Fn`] + /// + /// Most often created by using the implementation by using the implementation of + /// [`From`] + /// + /// If you're feeling overwhelmed by the function signature, don't panic. Please see + /// the [example](#example). + /// + /// Any requests passed to the handler will be directly handed down to the handler, + /// with the request as the first argument. The response provided will be sent to the + /// requester. If the handler panics or returns an [`Err`], this will be logged, and + /// the requester will be sent a [`TEMPORARY FAILURE`](Response::temporary_failure()). + /// + /// [`From`]: #impl-From FnHandler(HandlerInner), /// A handler that always serves an identical response, for any and all request + /// + /// Any and all requests to this handler will be responded to with the same response, + /// no matter what. This is good for static content that is provided by your app. + /// For serving files & directories, try looking at creating a [`FilesHandler`] by + /// [passing a directory](#impl-From). + /// + /// Most often created by using [`From`] or [`From`] + /// + /// [`FilesHandler`]: Self::FilesHandler + /// [`From`]: #impl-From + /// [`From`]: #impl-From<%26'_%20Document> StaticHandler(Response), #[cfg(feature = "serve_dir")] /// A handler that serves a directory, including a directory listing + /// + /// Most often created with [`From`] + /// + /// Any requests directed to this handler will be served from this path. For example, + /// if a handler serving files from the path `./public/` and bound to `/serve` + /// receives a request for `/serve/file.txt`, it will respond with the contents of the + /// file at `./public/file.txt`, and automatically infer the MIME type. + /// + /// This is equivilent to serving files using [`util::serve_dir()`], and as such will + /// include directory listings. + /// + /// Additionally, if the path is only a single file, that file will be served in + /// response to *every request*. That is, adding a handler for `/path/to/file.txt` + /// to the route `/hello` will mean that `/hello`, `/hello/file.txt`, and + /// `/hello/irrele/vant` will all be responded to with the contents of `file.txt`. + /// + /// [`From`]: #impl-From FilesHandler(PathBuf), } @@ -102,14 +137,28 @@ where H: 'static + Fn(Request) -> R + Send + Sync, R: 'static + Future> + Send, { - /// Wrap an [`Fn`] in a [`Handler`] struct + /// Wrap an [`Fn`] in a [`Handler`] struct, creating an [`FnHandler`] /// /// This automatically boxes both the [`Fn`] and the [`Fn`]'s response. /// - /// Any requests passed to the handler will be directly handed down to the handler, - /// with the request as the first argument. The response provided will be sent to the - /// requester. If the handler panics or returns an [`Err`], this will be logged, and - /// the requester will be sent a [`TEMPORARY FAILURE`](Response::temporary_failure()). + /// Don't be overwhelmed by the function signature here. It's honestly way simpler + /// than it looks. + /// + /// # Example + /// + /// ``` + /// # use kochab::*; + /// use anyhow::Result; + /// + /// let handler: Handler = handle_request.into(); + /// + /// async fn handle_request(request: Request) -> Result { + /// // This could be done with a StaticHandler, but for demonstration: + /// Ok(Response::success_gemini("Hello world!")) + /// } + /// ``` + /// + /// [`FnHandler`]: Self::FnHandler fn from(handler: H) -> Self { Self::FnHandler( Box::new(move|req| Box::pin((handler)(req)) as HandlerResponse) @@ -123,15 +172,14 @@ where impl From for Handler { /// Serve an unchanging response /// - /// Any and all requests to this handler will be responded to with the same response, - /// no matter what. This is good for static content that is provided by your app. - /// For serving files & directories, try looking at creating a handler from a path - /// /// ## Panics /// This response type **CANNOT** be created using Responses with [`Reader`] bodies. /// Attempting to do this will cause a panic. Don't. /// + /// This will create a [`StaticHandler`] + /// /// [`Reader`]: Body::Reader + /// [`StaticHandler`]: Self::StaticHandler fn from(response: Response) -> Self { #[cfg(debug_assertions)] { // We have another check once the handler is actually called that is not @@ -150,6 +198,10 @@ impl From<&Document> for Handler { /// This document will be sent in response to any requests that arrive at this /// handler. As with all documents, this will be a successful response with a /// `text/gemini` MIME. + /// + /// This will create a [`StaticHandler`] + /// + /// [`StaticHandler`]: Self::StaticHandler fn from(doc: &Document) -> Self { Self::StaticHandler(doc.into()) } @@ -159,18 +211,13 @@ impl From<&Document> for Handler { impl From for Handler { /// Serve files from a directory /// - /// Any requests directed to this handler will be served from this path. For example, - /// if a handler serving files from the path `./public/` and bound to `/serve` - /// receives a request for `/serve/file.txt`, it will respond with the contents of the - /// file at `./public/file.txt`. - /// - /// This is equivilent to serving files using [`util::serve_dir()`], and as such will - /// include directory listings. - /// /// The path to a single file can be passed in order to serve only a single file for /// any and all requests. /// + /// This will create a [`FilesHandler`]. + /// /// [`util::serve_dir()`]: crate::util::serve_dir() + /// [`FilesHandler`]: Handler::FilesHandler fn from(path: PathBuf) -> Self { Self::FilesHandler(path) } diff --git a/src/lib.rs b/src/lib.rs index c347670..8a87cd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -230,11 +230,11 @@ use tokio_rustls::TlsAcceptor; use rustls::Session; mod types; +mod handling; pub mod util; pub mod routing; -pub mod handling; #[cfg(feature = "ratelimiting")] -pub mod ratelimiting; +mod ratelimiting; #[cfg(feature = "user_management")] pub mod user_management; #[cfg(feature = "gemini_srv")] @@ -247,6 +247,7 @@ pub use cert::CertGenMode; pub use uriparse as uri; pub use types::*; +pub use handling::Handler; /// The maximun length of a Request URI pub const REQUEST_URI_MAX_LEN: usize = 1024; @@ -254,8 +255,6 @@ pub const REQUEST_URI_MAX_LEN: usize = 1024; /// The default port for the gemini protocol pub const GEMINI_PORT: u16 = 1965; -use handling::Handler; - #[derive(Clone)] struct ServerInner { #[cfg(feature = "gemini_srv")] diff --git a/src/routing.rs b/src/routing.rs index 7b12870..07f63a7 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -1,6 +1,12 @@ //! Utilities for routing requests //! +//! For most users, this is more advanced than you need to get. If you're interested in +//! adding routes, please see [`Server::add_route()`], which is how most people will +//! interact with the routing API +//! //! See [`RoutingNode`] for details on how routes are matched. +//! +//! [`Server::add_route()`]: crate::Server::add_route use uriparse::path::{Path, Segment};