Skip to main content

volter_core/
lib.rs

1//! Core traits and types for Volter.
2//!
3//! This crate defines the foundational abstractions the rest of the Volter
4//! ecosystem builds on:
5//!
6//! - [`IntoResponse`] — how values turn into HTTP responses.
7//! - [`FromRequestParts`] / [`FromRequest`] — typed extraction from requests.
8//! - [`Handler`] — async function handlers.
9//! - [`HandlerService`] — a [`tower::Service`] adapter for handlers.
10//! - [`Body`], [`BoxBody`], [`Request`], [`Response`] — core HTTP type aliases.
11//!
12//! See `ARCHITECTURE.md` at the workspace root for the reasoning behind
13//! this design, and `RULES.md` for the constraints every implementation
14//! must follow.
15
16#![deny(missing_docs)]
17#![deny(
18    clippy::unwrap_used,
19    clippy::expect_used,
20    clippy::panic,
21    clippy::indexing_slicing
22)]
23
24mod body;
25mod extract;
26mod handler;
27mod into_response;
28mod service;
29mod url_params;
30
31pub use body::{empty_body, full_body, Body, BoxBody, BoxError, Request, Response};
32pub use extract::{FromRequest, FromRequestParts, State};
33pub use handler::Handler;
34pub use into_response::IntoResponse;
35pub use service::HandlerService;
36pub use url_params::UrlParams;
37
38/// Re-export of the `http` crate so downstream users can refer to common
39/// HTTP types ( [`http::StatusCode`], [`http::Method`], etc.) through
40/// `volter_core::http`.
41pub use http;
42
43// ---------------------------------------------------------------------------
44// Tests
45// ---------------------------------------------------------------------------
46
47#[cfg(test)]
48mod tests {
49    #![allow(clippy::unwrap_used, clippy::expect_used)]
50
51    use super::*;
52    use crate::body::empty_body;
53    use http::StatusCode;
54    use std::convert::Infallible;
55    use tower::Service;
56
57    // -- IntoResponse tests --------------------------------------------------
58
59    #[test]
60    fn response_passthrough() {
61        let original = Response::new(empty_body());
62        let response = original.into_response();
63        assert_eq!(response.status(), StatusCode::OK);
64    }
65
66    #[test]
67    fn status_code_response() {
68        let response = StatusCode::NOT_FOUND.into_response();
69        assert_eq!(response.status(), StatusCode::NOT_FOUND);
70    }
71
72    #[test]
73    fn str_response() {
74        let response = "hello".into_response();
75        assert_eq!(response.status(), StatusCode::OK);
76    }
77
78    #[test]
79    fn string_response() {
80        let response = String::from("hello").into_response();
81        assert_eq!(response.status(), StatusCode::OK);
82    }
83
84    #[test]
85    fn tuple_status_response() {
86        let response = (StatusCode::CREATED, "body").into_response();
87        assert_eq!(response.status(), StatusCode::CREATED);
88    }
89
90    #[test]
91    fn unit_into_response() {
92        let response = ().into_response();
93        assert_eq!(response.status(), StatusCode::NO_CONTENT);
94    }
95
96    #[test]
97    fn result_ok_into_response() {
98        let response: Result<&'static str, StatusCode> = Ok("hello");
99        let resp = response.into_response();
100        assert_eq!(resp.status(), StatusCode::OK);
101    }
102
103    #[test]
104    fn result_err_into_response() {
105        let response: Result<(), StatusCode> = Err(StatusCode::BAD_REQUEST);
106        let resp = response.into_response();
107        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
108    }
109
110    // -- Handler / HandlerService tests --------------------------------------
111
112    #[tokio::test]
113    async fn zero_arg_handler() {
114        async fn greet() -> &'static str {
115            "hello"
116        }
117
118        let response = greet().await.into_response();
119        assert_eq!(response.status(), StatusCode::OK);
120    }
121
122    #[tokio::test]
123    async fn handler_service_call() -> Result<(), Infallible> {
124        async fn answer() -> &'static str {
125            "forty-two"
126        }
127
128        let mut service = HandlerService::new(answer, ());
129        let request = http::Request::new(empty_body());
130        let response = service.call(request).await?;
131        assert_eq!(response.status(), StatusCode::OK);
132        Ok(())
133    }
134
135    #[tokio::test]
136    async fn handler_service_oneshot() -> Result<(), Infallible> {
137        async fn ping() -> &'static str {
138            "pong"
139        }
140
141        let service = HandlerService::new(ping, ());
142        let request = http::Request::new(empty_body());
143        let response = tower::ServiceExt::oneshot(service, request).await?;
144        assert_eq!(response.status(), StatusCode::OK);
145        Ok(())
146    }
147}