1#![deny(missing_docs)]
111#![deny(rustdoc::broken_intra_doc_links)]
112
113pub mod binding;
114pub mod bootstrap;
115pub mod broadcasting;
116pub mod commands;
117pub mod facades;
118pub mod groups;
119pub mod health;
120#[cfg(feature = "kafka")]
121pub mod kafka;
122pub mod keys;
123pub mod limits;
124pub mod notifications;
125pub mod observability;
126pub mod queued_listeners;
127#[cfg(feature = "kafka")]
128pub mod relay;
129pub mod scheduling;
130pub mod signed;
131pub mod testing;
132
133pub use bootstrap::Rainier;
134pub use commands::{
135 console, KeyGenerateCommand, MigrateCommand, MigrateRollbackCommand, QueueWorkCommand,
136 RouteListCommand, ServeCommand,
137};
138pub use facades::Views;
139pub use health::{Health, Report as HealthReport};
140pub use limits::RateLimits;
141pub use queued_listeners::{DispatcherExt, FromEvent};
142pub use scheduling::{
143 CommandTask, JobTask, ScheduleExt, ScheduleListCommand, ScheduleRunCommand, ScheduleWorkCommand,
144};
145pub use signed::{SignedUrls, ValidateSignature};
146pub use testing::{TestApp, TestResponse};
147
148pub use rainier_auth as auth;
151pub use rainier_broadcast as broadcast;
152pub use rainier_cache as cache;
153pub use rainier_config as config;
154pub use rainier_console as console_kernel;
155pub use rainier_container as container;
156pub use rainier_crypt as crypt;
157pub use rainier_database as database;
158pub use rainier_drivers as drivers;
159pub use rainier_events as events;
160pub use rainier_filesystem as filesystem;
161pub use rainier_http as http;
162pub use rainier_http_client as http_client;
163pub use rainier_mail as mail;
164pub use rainier_metrics as metrics;
165pub use rainier_middleware as middleware;
166pub use rainier_notify as notify;
167pub use rainier_openapi as openapi;
168pub use rainier_queue as queue;
169pub use rainier_routing as routing;
170pub use rainier_scheduler as scheduler;
171pub use rainier_server as server;
172pub use rainier_session as session;
173pub use rainier_support as support;
174pub use rainier_telemetry as telemetry;
175pub use rainier_validation as validation;
176pub use rainier_view as view;
177pub use rainier_websocket as websocket;
178
179pub use rainier_orm;
181
182pub use rainier_container::{facade, Application, Facade, ServiceProvider};
185pub use rainier_database::bind_executor;
186
187pub mod prelude {
193 pub use crate::bootstrap::Rainier;
194 pub use crate::facades::{
195 Broadcast, Cache, Config, Crypt, Event, Mail, Middleware, Notify, Queue, Session, Storage,
196 Url, View, Views, DB,
197 };
198
199 pub use rainier_container::{Application, Container, Facade, ServiceProvider};
200 pub use rainier_support::{build_info, BuildInfo, Context as _, Error, ErrorKind, Result};
201
202 pub use rainier_config::{AppEnv, Key as ConfigurationKey, Setting as _};
206 pub use rainier_telemetry::LogFormat;
207
208 pub use crate::binding::{Bound, BoundAs};
209 pub use rainier_http::extract::{Bearer, Form, Json, Path, Query, RawBody};
210 pub use rainier_http::{
211 Cookie, FromRequest, Html, IntoResponse, Method, Redirect, Request, Response, StatusCode,
212 };
213
214 pub use rainier_cache::{Cache as CacheContract, CacheDriver, CacheExt as _, CacheManager};
215 pub use rainier_crypt::{EncrypterExt as _, Encryption, SignerExt as _};
216 pub use rainier_filesystem::{
217 Filesystem, FilesystemDriver, FilesystemExt as _, LocalFilesystem,
218 Storage as StorageManager,
219 };
220 pub use rainier_http_client::{Backoff, Http};
221 pub use rainier_middleware::{
222 Compress, MethodOverride, Middleware as MiddlewareContract, Next, Timeout, TrustProxies,
223 };
224 pub use rainier_routing::{
225 GroupAttributes, ParamConstraint, Req, ResourceController, Route, Router,
226 };
227 pub use rainier_session::{
228 Session as SessionBag, SessionDriver, SessionManager, SessionRequestExt as _, StartSession,
229 };
230 pub use rainier_validation::{field, FormRequest, Rule, RuleSet, Validated, Validator};
231
232 pub use rainier_database::{
233 BelongsTo, BelongsToMany, Criteria, Database, EntityRepository, HasMany, HasOne, Model,
234 Paginated, Related, RelatedCounts, Relation, Repository,
235 };
236 pub use rainier_orm::Entity;
237
238 pub use rainier_auth::{
239 AuthManager, Authenticatable, AuthenticatedUser, Credentials, Gate, GuardExt as _,
240 };
241 pub use rainier_broadcast::{Broadcastable, Broadcasting, Channel as BroadcastChannelName};
242 pub use rainier_events::Dispatcher;
243 pub use rainier_mail::{Content, Envelope, MailDriver, Mailable};
244 pub use rainier_metrics::{Metrics, RecordMetrics};
245 pub use rainier_notify::{Channels, Notifiable, Notification, Notifier};
246 pub use rainier_queue::{Job, JobContext, QueueDriver, QueueManager};
247 pub use rainier_telemetry::Trace;
248 pub use rainier_view::View as ViewData;
249 pub use rainier_websocket::{Rooms, Socket, WebSocketHandler, WebSocketRoutes};
250
251 pub use async_trait::async_trait;
252 pub use std::sync::Arc;
253}
254
255#[cfg(test)]
256mod tests {
257 use crate::prelude::*;
258 use rainier_http::Method;
259
260 #[tokio::test]
261 async fn an_application_boots_and_serves() {
262 let app = Rainier::new(".")
263 .without_facades()
264 .without_tracing()
265 .with_routes(|router| {
266 router.get("/", || async { "hello" }).name("home");
267 })
268 .boot()
269 .await
270 .expect("boots");
271
272 let kernel = app.resolve::<rainier_server::Kernel>().unwrap();
273 let response =
274 kernel.handle_request(Request::builder().method(Method::GET).uri("/").build()).await;
275
276 assert_eq!(response.status(), StatusCode::OK);
277 }
278
279 #[tokio::test]
280 async fn the_prelude_covers_a_realistic_controller() {
281 #[derive(serde::Deserialize)]
285 struct StorePost {
286 title: String,
287 }
288
289 #[async_trait]
290 impl FormRequest for StorePost {
291 fn rules() -> RuleSet {
292 vec![field("title", [Rule::Required, Rule::String])]
293 }
294 }
295
296 async fn store(Validated(post): Validated<StorePost>) -> Result<Response> {
297 Ok(Response::json(&serde_json::json!({ "title": post.title })))
298 }
299
300 let app = Rainier::new(".")
301 .without_facades()
302 .without_tracing()
303 .with_routes(|router| {
304 router.post("/posts", store);
305 })
306 .boot()
307 .await
308 .expect("boots");
309
310 let response = app
311 .resolve::<rainier_server::Kernel>()
312 .unwrap()
313 .handle_request(
314 Request::builder()
315 .method(Method::POST)
316 .uri("/posts")
317 .json(&serde_json::json!({ "title": "Hello" }))
318 .build(),
319 )
320 .await;
321
322 assert_eq!(response.status(), StatusCode::OK);
323 }
324
325 #[tokio::test]
326 async fn a_failed_contract_is_a_422() {
327 #[derive(serde::Deserialize)]
328 struct StorePost {
329 #[allow(dead_code)]
330 title: String,
331 }
332
333 #[async_trait]
334 impl FormRequest for StorePost {
335 fn rules() -> RuleSet {
336 vec![field("title", [Rule::Required])]
337 }
338 }
339
340 async fn store(Validated(_): Validated<StorePost>) -> &'static str {
341 "created"
342 }
343
344 let app = Rainier::new(".")
345 .without_facades()
346 .without_tracing()
347 .with_routes(|router| {
348 router.post("/posts", store);
349 })
350 .boot()
351 .await
352 .unwrap();
353
354 let response = app
355 .resolve::<rainier_server::Kernel>()
356 .unwrap()
357 .handle_request(
358 Request::builder()
359 .method(Method::POST)
360 .uri("/posts")
361 .header("accept", "application/json")
362 .json(&serde_json::json!({}))
363 .build(),
364 )
365 .await;
366
367 assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
368 }
369}