This repository has been archived by the owner on Dec 9, 2024. It is now read-only.
generated from clechasseur/rust-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib.rs
144 lines (134 loc) · 5.58 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! # Rust Pokédex API 🦀
//!
//! This project implements a simple web application that contains a CRUD API for a Pokédex - a database of [Pokémons](https://en.wikipedia.org/wiki/Pok%C3%A9mon).
//! It is meant as an experiment in building fully-working web applications in Rust.
//!
//! This project includes several components usually found in modern web applications, including:
//!
//! - A high-performance HTTP server to handle incoming requests
//! - A REST API with CRUD endpoints for Pokémon entities
//! - Automatic serialization/deserialization of Pokémon entities as JSON
//! - Automatic OpenAPI documentation including Swagger UI support (and others)
//! - An ORM-like interface to persist Pokémons in a Postgres database
//! - Support for managing and applying database migrations
//! - Validation of incoming data at the endpoint level
//! - Database connection pooling to improve performance
//! - Configurable logging using a simple logging facade
//! - Error handling with separation between service errors and their HTTP response counterparts
//! - Support for development and production environments
//!
//! This documentation is for the private lib crate implementing the types and functions required
//! to implement the CRUD REST API for the Pokedex. For more details about the project, see the
//! [project's GitHub repository](https://github.com/clechasseur/pokerust).
//!
//! # Notes
//!
//! Pretty much everything in this crate is `pub`lic. This would not normally be the case, but
//! it was done for demo purposes, so that it's easier to document the various pieces.
#![cfg_attr(backtrace_support, feature(error_generic_member_access))]
#![deny(missing_docs)]
#![deny(rustdoc::missing_crate_level_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
pub mod api;
pub mod db;
pub mod error;
pub mod helpers;
pub mod models;
#[doc(hidden)]
#[cfg(not(tarpaulin_include))]
pub mod schema;
pub mod service_env;
pub mod services;
use actix_web::web;
use actix_web::web::ServiceConfig;
use actix_web_validator::{JsonConfig, PathConfig, QueryConfig};
pub use error::Error;
pub use error::Result;
use log::trace;
use crate::api::errors::input_error_handler;
use crate::db::Pool;
use crate::error::InputErrorContext;
/// Macro that expands to an [`App`] instance, initialized for our web application.
///
/// For the main binary crate, pass the [`App`] as factory to [`HttpServer::new`] to
/// serve the app's endpoints over HTTP. For tests, pass the [`App`] to [`test::init_service`]
/// to initialize a test service.
///
/// # Notes
///
/// It is possible to further modify the [`App`] generated by this macro. For example:
///
/// ```no_run
/// # use actix_web::{HttpResponse, web};
/// # use pokedex_rs::db::get_pool;
/// # use pokedex_rs::pokedex_app;
/// #
/// # let pool = get_pool().unwrap();
/// // let pool = ...;
/// let app = pokedex_app!(pool).route("/", web::get().to(|| HttpResponse::Ok()));
/// ```
///
/// [`App`]: actix_web::App
/// [`HttpServer::new`]: actix_web::HttpServer::new
/// [`test::init_service`]: actix_web::test::init_service
#[macro_export]
macro_rules! pokedex_app {
($pool:expr) => {
actix_web::App::new()
.wrap(actix_web::middleware::Logger::default())
.app_data($crate::get_json_config())
.app_data($crate::get_path_config())
.app_data($crate::get_query_config())
.configure($crate::configure_api(&($pool)))
};
}
/// Allows registration of the entire Pokedex API under the `/api` scope.
///
/// Do not use this function directly; instead, use the [`pokedex_app!`] macro to initialize an
/// [`App`](actix_web::App) instance.
pub fn configure_api(pool: &Pool) -> impl FnOnce(&mut ServiceConfig) + '_ {
|config| {
trace!("Configuring Pokedex API");
trace!("Adding API endpoints for /");
config
.service(web::scope("/api").configure(api::configure(pool)))
.configure(api::doc::configure);
}
}
/// Returns the [`JsonConfig`] to use for our service.
///
/// This config will register a custom error handler that will handle input errors
/// using our [`ResponseError` impl](Error#impl-ResponseError-for-Error).
///
/// # Notes
///
/// This function cannot be generic over the config type, because unfortunately `actix-web-validator`'s
/// various config types do not share a common trait that has the `error_handler` method.
pub fn get_json_config() -> JsonConfig {
JsonConfig::default().error_handler(input_error_handler(InputErrorContext::Json))
}
/// Returns the [`PathConfig`] to use for our service.
///
/// This config will register a custom error handler that will handle input errors
/// using our [`ResponseError` impl](Error#impl-ResponseError-for-Error).
///
/// # Notes
///
/// This function cannot be generic over the config type, because unfortunately `actix-web-validator`'s
/// various config types do not share a common trait that has the `error_handler` method.
pub fn get_path_config() -> PathConfig {
PathConfig::default().error_handler(input_error_handler(InputErrorContext::Path))
}
/// Returns the [`QueryConfig`] to use for our service.
///
/// This config will register a custom error handler that will handle input errors
/// using our [`ResponseError` impl](Error#impl-ResponseError-for-Error).
///
/// # Notes
///
/// This function cannot be generic over the config type, because unfortunately `actix-web-validator`'s
/// various config types do not share a common trait that has the `error_handler` method.
pub fn get_query_config() -> QueryConfig {
QueryConfig::default().error_handler(input_error_handler(InputErrorContext::Query))
}