serverust_core/route.rs
1use axum::routing::MethodRouter;
2use utoipa::openapi::{HttpMethod, path::Operation};
3
4use crate::container::Container;
5
6/// Metadata de uma rota registrável no [`crate::App`].
7///
8/// O `MethodRouter` é parametrizado pelo [`Container`] (state do App) para
9/// que handlers possam extrair serviços via `State<Arc<dyn Trait>>`.
10pub struct Route {
11 pub path: &'static str,
12 pub method: HttpMethod,
13 pub method_router: MethodRouter<Container>,
14 pub operation: Operation,
15 /// Se a rota dispensa autenticação.
16 ///
17 /// Default `false` — o default deny da ADR 0009. Com autenticação
18 /// instalada, [`crate::App::route`] embrulha toda rota que **não** tenha
19 /// isto ligado com o [`crate::AuthGate`]. Ligue via [`Route::public`].
20 pub is_public: bool,
21 /// Escopos que a rota exige, para o `security` do OpenAPI.
22 ///
23 /// Preenchido pela macro `#[authorize]`. **Não é o que aplica a
24 /// exigência** — quem faz isso é o `Guard` que a macro injeta. Aqui é só
25 /// o que o documento precisa declarar, para que o botão "Authorize" do
26 /// Scalar/Swagger UI peça os escopos certos.
27 ///
28 /// Separar as duas coisas é o que permite documentar sem mexer na trait
29 /// [`crate::Guard`]; o custo é que um guard escrito à mão não aparece no
30 /// documento, por não ter como declarar o que exige.
31 pub required_scopes: &'static [&'static str],
32}
33
34impl Route {
35 pub fn new(
36 path: &'static str,
37 method: HttpMethod,
38 method_router: MethodRouter<Container>,
39 operation: Operation,
40 ) -> Self {
41 Self {
42 path,
43 method,
44 method_router,
45 operation,
46 is_public: false,
47 required_scopes: &[],
48 }
49 }
50
51 /// Marca a rota como pública: ela responde mesmo sem identidade.
52 ///
53 /// É a exceção explícita ao default deny — o equivalente do `pub` do Rust
54 /// para rotas. Marcar o que é aberto, em vez do que é protegido, é o que
55 /// torna a superfície exposta auditável por presença: `grep` encontra
56 /// marcação, nunca a falta dela.
57 ///
58 /// ```
59 /// # use serverust_core::Route;
60 /// # use axum::routing::get;
61 /// # use utoipa::openapi::{HttpMethod, path::Operation};
62 /// let route = Route::new("/health", HttpMethod::Get, get(|| async {}), Operation::new())
63 /// .public();
64 /// assert!(route.is_public);
65 /// ```
66 pub fn public(mut self) -> Self {
67 self.is_public = true;
68 self
69 }
70
71 /// Declara os escopos que a rota exige, para o `security` do OpenAPI.
72 ///
73 /// Chamada pela macro `#[authorize]`. Chamar à mão documenta a exigência
74 /// sem aplicá-la — o que é pior do que não documentar, porque passa a
75 /// mentir. Use junto com o guard que realmente verifica.
76 pub fn scopes(mut self, scopes: &'static [&'static str]) -> Self {
77 self.required_scopes = scopes;
78 self
79 }
80}
81
82/// Implementado pelas structs geradas pelas macros `#[get]`, `#[post]`, etc.
83///
84/// Permite passar o nome do handler diretamente para `App::route(handler)`.
85pub trait IntoRoute {
86 fn into_route(self) -> Route;
87}