Skip to content

Commit 82da03b

Browse files
committed
!
1 parent 27f67e2 commit 82da03b

File tree

11 files changed

+133
-174
lines changed

11 files changed

+133
-174
lines changed

examples/auth-api/src/main.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ async fn login(
151151
// ============================================
152152

153153
/// Get current user's profile (requires authentication)
154+
#[rustapi_rs::get("/protected/profile")]
155+
#[rustapi_rs::tag("Protected")]
156+
#[rustapi_rs::summary("Get Profile")]
154157
async fn get_profile(AuthUser(claims): AuthUser<Claims>) -> Json<UserProfile> {
155158
Json(UserProfile {
156159
user_id: claims.sub,
@@ -160,6 +163,9 @@ async fn get_profile(AuthUser(claims): AuthUser<Claims>) -> Json<UserProfile> {
160163
}
161164

162165
/// Admin-only endpoint
166+
#[rustapi_rs::get("/protected/admin")]
167+
#[rustapi_rs::tag("Protected")]
168+
#[rustapi_rs::summary("Admin Only")]
163169
async fn admin_only(AuthUser(claims): AuthUser<Claims>) -> Result<Json<Message>, ApiError> {
164170
if claims.role != "admin" {
165171
return Err(ApiError::forbidden("Admin access required"));
@@ -171,6 +177,9 @@ async fn admin_only(AuthUser(claims): AuthUser<Claims>) -> Result<Json<Message>,
171177
}
172178

173179
/// Protected data endpoint
180+
#[rustapi_rs::get("/protected/data")]
181+
#[rustapi_rs::tag("Protected")]
182+
#[rustapi_rs::summary("Protected Data")]
174183
async fn get_protected_data(AuthUser(claims): AuthUser<Claims>) -> Json<Message> {
175184
Json(Message {
176185
message: format!("Secret data for user: {}", claims.username),
@@ -203,7 +212,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
203212
// Create the app with JWT middleware for protected routes
204213
// Public routes (/health, /auth/login, /) are excluded from JWT validation
205214
// Docs has its own Basic Auth protection
206-
let app = RustApi::new()
215+
// Phase 6 / zero-config: routes + schemas are auto-registered via macros.
216+
// We use `config().build()` to auto-mount routes first, then attach docs with Basic Auth.
217+
let app = RustApi::config()
218+
.docs_enabled(false)
207219
.body_limit(1024 * 1024) // 1MB limit
208220
.layer(RequestIdLayer::new())
209221
.layer(TracingLayer::new())
@@ -216,18 +228,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
216228
"/auth/login",
217229
"/",
218230
]))
219-
.register_schema::<LoginRequest>()
220-
.register_schema::<LoginResponse>()
221-
.register_schema::<UserProfile>()
222-
.register_schema::<Message>()
223-
// Public routes
224-
.mount_route(welcome_route())
225-
.mount_route(health_route())
226-
.mount_route(login_route())
227-
// Protected routes
228-
.route("/protected/profile", get(get_profile))
229-
.route("/protected/admin", get(admin_only))
230-
.route("/protected/data", get(get_protected_data))
231+
.build()
231232
// Docs with Basic Auth protection
232233
.docs_with_auth("/docs", "docs", "docs123");
233234

examples/crud-api/src/main.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -322,24 +322,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
322322
println!();
323323
println!("Server running at http://127.0.0.1:8080");
324324

325-
RustApi::new()
325+
// Phase 6 / zero-config: routes + schemas are auto-registered via macros.
326+
// Swagger UI is enabled at /docs by default (when built with swagger-ui feature).
327+
RustApi::auto()
326328
.state(store)
327329
.body_limit(1024 * 1024) // 1MB limit
328330
.layer(RequestIdLayer::new())
329331
.layer(TracingLayer::new())
330-
.register_schema::<Task>()
331-
.register_schema::<CreateTask>()
332-
.register_schema::<UpdateTask>()
333-
.register_schema::<PatchTask>()
334-
.register_schema::<PaginatedTasks>()
335-
.mount_route(list_tasks_route())
336-
.mount_route(get_task_route())
337-
.mount_route(create_task_route())
338-
.mount_route(update_task_route())
339-
.mount_route(patch_task_route())
340-
.mount_route(delete_task_route())
341-
.mount_route(health_route())
342-
.docs("/docs")
343332
.run("127.0.0.1:8080")
344333
.await
345334
}

examples/hello-world/src/main.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
117117
println!(" GET /docs - Swagger UI");
118118
println!();
119119

120-
RustApi::new()
121-
.register_schema::<UserResponse>()
122-
.register_schema::<CreateUser>()
123-
.mount_route(hello_route())
124-
.mount_route(health_route())
125-
.mount_route(get_user_route())
126-
.mount_route(create_user_route())
127-
.mount_route(search_users_route())
128-
.docs("/docs") // Enable Swagger UI!
129-
.run("127.0.0.1:8080")
130-
.await
120+
// Phase 6 / zero-config: routes + schemas are auto-registered via macros.
121+
// Swagger UI is enabled at /docs by default (when built with swagger-ui feature).
122+
RustApi::auto().run("127.0.0.1:8080").await
131123
}

examples/proof-of-concept/src/handlers/auth.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use crate::stores::AppState;
1010
use crate::{JWT_SECRET, TOKEN_EXPIRY_SECS};
1111

1212
/// Register a new user
13+
#[rustapi_rs::post("/auth/register")]
14+
#[rustapi_rs::tag("Auth")]
15+
#[rustapi_rs::summary("Register")]
1316
async fn register(
1417
State(state): State<Arc<AppState>>,
1518
ValidatedJson(body): ValidatedJson<RegisterRequest>,
@@ -59,6 +62,9 @@ async fn register(
5962
}
6063

6164
/// Login with email and password
65+
#[rustapi_rs::post("/auth/login")]
66+
#[rustapi_rs::tag("Auth")]
67+
#[rustapi_rs::summary("Login")]
6268
async fn login(
6369
State(state): State<Arc<AppState>>,
6470
ValidatedJson(body): ValidatedJson<LoginRequest>,
@@ -103,12 +109,3 @@ async fn login(
103109
},
104110
}))
105111
}
106-
107-
// Route functions
108-
pub fn register_route() -> Route {
109-
post_route("/auth/register", register)
110-
}
111-
112-
pub fn login_route() -> Route {
113-
post_route("/auth/login", login)
114-
}

examples/proof-of-concept/src/handlers/bookmarks.rs

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ use crate::models::{
1111
use crate::stores::AppState;
1212

1313
/// List bookmarks with pagination and filtering
14+
#[rustapi_rs::get("/bookmarks")]
15+
#[rustapi_rs::tag("Bookmarks")]
16+
#[rustapi_rs::summary("List Bookmarks")]
1417
async fn list_bookmarks(
1518
State(state): State<Arc<AppState>>,
1619
AuthUser(claims): AuthUser<Claims>,
@@ -66,6 +69,9 @@ async fn list_bookmarks(
6669
}
6770

6871
/// Create a new bookmark
72+
#[rustapi_rs::post("/bookmarks")]
73+
#[rustapi_rs::tag("Bookmarks")]
74+
#[rustapi_rs::summary("Create Bookmark")]
6975
async fn create_bookmark(
7076
State(state): State<Arc<AppState>>,
7177
AuthUser(claims): AuthUser<Claims>,
@@ -98,6 +104,9 @@ async fn create_bookmark(
98104
}
99105

100106
/// Get a single bookmark by ID
107+
#[rustapi_rs::get("/bookmarks/{id}")]
108+
#[rustapi_rs::tag("Bookmarks")]
109+
#[rustapi_rs::summary("Get Bookmark")]
101110
async fn get_bookmark(
102111
State(state): State<Arc<AppState>>,
103112
AuthUser(claims): AuthUser<Claims>,
@@ -120,6 +129,9 @@ async fn get_bookmark(
120129
}
121130

122131
/// Update a bookmark
132+
#[rustapi_rs::put("/bookmarks/{id}")]
133+
#[rustapi_rs::tag("Bookmarks")]
134+
#[rustapi_rs::summary("Update Bookmark")]
123135
async fn update_bookmark(
124136
State(state): State<Arc<AppState>>,
125137
AuthUser(claims): AuthUser<Claims>,
@@ -174,6 +186,9 @@ async fn update_bookmark(
174186
}
175187

176188
/// Delete a bookmark
189+
#[rustapi_rs::delete("/bookmarks/{id}")]
190+
#[rustapi_rs::tag("Bookmarks")]
191+
#[rustapi_rs::summary("Delete Bookmark")]
177192
async fn delete_bookmark(
178193
State(state): State<Arc<AppState>>,
179194
AuthUser(claims): AuthUser<Claims>,
@@ -203,6 +218,9 @@ async fn delete_bookmark(
203218
}
204219

205220
/// Export bookmarks as JSON
221+
#[rustapi_rs::get("/bookmarks/export")]
222+
#[rustapi_rs::tag("Bookmarks")]
223+
#[rustapi_rs::summary("Export Bookmarks")]
206224
async fn export_bookmarks(
207225
State(state): State<Arc<AppState>>,
208226
AuthUser(claims): AuthUser<Claims>,
@@ -235,6 +253,9 @@ async fn export_bookmarks(
235253
}
236254

237255
/// Import bookmarks from JSON
256+
#[rustapi_rs::post("/bookmarks/import")]
257+
#[rustapi_rs::tag("Bookmarks")]
258+
#[rustapi_rs::summary("Import Bookmarks")]
238259
async fn import_bookmarks(
239260
State(state): State<Arc<AppState>>,
240261
AuthUser(claims): AuthUser<Claims>,
@@ -294,32 +315,3 @@ async fn import_bookmarks(
294315
errors,
295316
})
296317
}
297-
298-
// Route functions
299-
pub fn list_bookmarks_route() -> Route {
300-
get_route("/bookmarks", list_bookmarks)
301-
}
302-
303-
pub fn create_bookmark_route() -> Route {
304-
post_route("/bookmarks", create_bookmark)
305-
}
306-
307-
pub fn get_bookmark_route() -> Route {
308-
get_route("/bookmarks/{id}", get_bookmark)
309-
}
310-
311-
pub fn update_bookmark_route() -> Route {
312-
put_route("/bookmarks/{id}", update_bookmark)
313-
}
314-
315-
pub fn delete_bookmark_route() -> Route {
316-
delete_route("/bookmarks/{id}", delete_bookmark)
317-
}
318-
319-
pub fn export_bookmarks_route() -> Route {
320-
get_route("/bookmarks/export", export_bookmarks)
321-
}
322-
323-
pub fn import_bookmarks_route() -> Route {
324-
post_route("/bookmarks/import", import_bookmarks)
325-
}

examples/proof-of-concept/src/handlers/categories.rs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use crate::models::{
1010
use crate::stores::AppState;
1111

1212
/// List categories
13+
#[rustapi_rs::get("/categories")]
14+
#[rustapi_rs::tag("Categories")]
15+
#[rustapi_rs::summary("List Categories")]
1316
async fn list_categories(
1417
State(state): State<Arc<AppState>>,
1518
AuthUser(claims): AuthUser<Claims>,
@@ -28,6 +31,9 @@ async fn list_categories(
2831
}
2932

3033
/// Create a new category
34+
#[rustapi_rs::post("/categories")]
35+
#[rustapi_rs::tag("Categories")]
36+
#[rustapi_rs::summary("Create Category")]
3137
async fn create_category(
3238
State(state): State<Arc<AppState>>,
3339
AuthUser(claims): AuthUser<Claims>,
@@ -59,6 +65,9 @@ async fn create_category(
5965
}
6066

6167
/// Update a category
68+
#[rustapi_rs::put("/categories/{id}")]
69+
#[rustapi_rs::tag("Categories")]
70+
#[rustapi_rs::summary("Update Category")]
6271
async fn update_category(
6372
State(state): State<Arc<AppState>>,
6473
AuthUser(claims): AuthUser<Claims>,
@@ -102,6 +111,9 @@ async fn update_category(
102111
}
103112

104113
/// Delete a category
114+
#[rustapi_rs::delete("/categories/{id}")]
115+
#[rustapi_rs::tag("Categories")]
116+
#[rustapi_rs::summary("Delete Category")]
105117
async fn delete_category(
106118
State(state): State<Arc<AppState>>,
107119
AuthUser(claims): AuthUser<Claims>,
@@ -128,20 +140,3 @@ async fn delete_category(
128140

129141
Ok(NoContent)
130142
}
131-
132-
// Route functions
133-
pub fn list_categories_route() -> Route {
134-
get_route("/categories", list_categories)
135-
}
136-
137-
pub fn create_category_route() -> Route {
138-
post_route("/categories", create_category)
139-
}
140-
141-
pub fn update_category_route() -> Route {
142-
put_route("/categories/{id}", update_category)
143-
}
144-
145-
pub fn delete_category_route() -> Route {
146-
delete_route("/categories/{id}", delete_category)
147-
}

examples/proof-of-concept/src/handlers/events.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use crate::stores::AppState;
88

99
/// SSE event stream endpoint - placeholder for now
1010
/// Real SSE implementation would require ResponseModifier for Sse type
11+
#[rustapi_rs::get("/events")]
12+
#[rustapi_rs::tag("Events")]
13+
#[rustapi_rs::summary("SSE Events")]
1114
async fn events(
1215
State(_state): State<Arc<AppState>>,
1316
AuthUser(_claims): AuthUser<Claims>,
@@ -18,8 +21,3 @@ async fn events(
1821
version: "SSE endpoint - use EventSource to connect".to_string(),
1922
})
2023
}
21-
22-
// Route function
23-
pub fn events_route() -> Route {
24-
get_route("/events", events)
25-
}

examples/proof-of-concept/src/handlers/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ use rustapi_rs::prelude::*;
1010
use crate::models::HealthResponse;
1111

1212
/// Health check endpoint
13+
#[rustapi_rs::get("/health")]
14+
#[rustapi_rs::tag("System")]
15+
#[rustapi_rs::summary("Health Check")]
1316
async fn health() -> Json<HealthResponse> {
1417
Json(HealthResponse {
1518
status: "ok".to_string(),
1619
version: env!("CARGO_PKG_VERSION").to_string(),
1720
})
1821
}
19-
20-
pub fn health_route() -> Route {
21-
get_route("/health", health)
22-
}

examples/proof-of-concept/src/main.rs

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
6262
// Initialize application state
6363
let state = Arc::new(AppState::new());
6464

65-
// Create the app
66-
let app = RustApi::new()
67-
.state(state)
65+
// Phase 6 / zero-config: routes + schemas are auto-registered via macros.
66+
// We use `config()` so we can attach layers/body limit while still using auto routes.
67+
let app = RustApi::config()
6868
.body_limit(1024 * 1024) // 1MB limit
6969
.layer(RequestIdLayer::new())
7070
.layer(TracingLayer::new())
@@ -77,42 +77,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
7777
"/auth/login",
7878
"/static",
7979
]))
80-
// Register schemas
81-
.register_schema::<models::RegisterRequest>()
82-
.register_schema::<models::LoginRequest>()
83-
.register_schema::<models::AuthResponse>()
84-
.register_schema::<models::UserInfo>()
85-
.register_schema::<models::CreateBookmarkRequest>()
86-
.register_schema::<models::UpdateBookmarkRequest>()
87-
.register_schema::<models::BookmarkResponse>()
88-
.register_schema::<models::CreateCategoryRequest>()
89-
.register_schema::<models::UpdateCategoryRequest>()
90-
.register_schema::<models::CategoryResponse>()
91-
.register_schema::<models::CategoryListResponse>()
92-
.register_schema::<models::ExportResponse>()
93-
.register_schema::<models::ErrorResponse>()
94-
// Auth routes
95-
.mount_route(handlers::auth::register_route())
96-
.mount_route(handlers::auth::login_route())
97-
// Bookmark routes
98-
.mount_route(handlers::bookmarks::list_bookmarks_route())
99-
.mount_route(handlers::bookmarks::create_bookmark_route())
100-
.mount_route(handlers::bookmarks::get_bookmark_route())
101-
.mount_route(handlers::bookmarks::update_bookmark_route())
102-
.mount_route(handlers::bookmarks::delete_bookmark_route())
103-
.mount_route(handlers::bookmarks::export_bookmarks_route())
104-
.mount_route(handlers::bookmarks::import_bookmarks_route())
105-
// Category routes
106-
.mount_route(handlers::categories::list_categories_route())
107-
.mount_route(handlers::categories::create_category_route())
108-
.mount_route(handlers::categories::update_category_route())
109-
.mount_route(handlers::categories::delete_category_route())
110-
// SSE events
111-
.mount_route(handlers::events::events_route())
112-
// Health check
113-
.mount_route(handlers::health_route())
114-
// Docs
115-
.docs("/docs");
80+
.build()
81+
.state(state);
11682

11783
app.run("127.0.0.1:8080").await
11884
}

0 commit comments

Comments
 (0)