1use fastapi_rust::prelude::*;
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct User {
27 pub id: u64,
28 pub name: String,
29 pub email: String,
30 pub role: UserRole,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
35pub enum UserRole {
36 Admin,
37 Editor,
38 Viewer,
39}
40
41#[derive(Debug, Deserialize)]
43pub struct CreateUser {
44 pub name: String,
45 pub email: String,
46 pub role: Option<UserRole>,
47}
48
49#[derive(Debug, Deserialize)]
51pub struct UpdateUser {
52 pub name: Option<String>,
53 pub email: Option<String>,
54 pub role: Option<UserRole>,
55}
56
57#[derive(Debug, Serialize)]
59pub struct ApiResponse<T: Serialize> {
60 pub data: T,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub message: Option<String>,
63}
64
65impl<T: Serialize> ApiResponse<T> {
66 pub fn ok(data: T) -> Self {
67 Self {
68 data,
69 message: None,
70 }
71 }
72
73 pub fn with_message(data: T, msg: impl Into<String>) -> Self {
74 Self {
75 data,
76 message: Some(msg.into()),
77 }
78 }
79}
80
81#[derive(Debug, Serialize)]
83pub struct PaginatedResponse<T: Serialize> {
84 pub items: Vec<T>,
85 pub total: u64,
86 pub page: u64,
87 pub per_page: u64,
88 pub total_pages: u64,
89}
90
91#[derive(Debug, Clone)]
97pub struct AppState {
98 pub app_name: String,
99 pub version: String,
100}
101
102impl Default for AppState {
103 fn default() -> Self {
104 Self {
105 app_name: "Demo API".into(),
106 version: env!("CARGO_PKG_VERSION").into(),
107 }
108 }
109}
110
111fn health_check() -> serde_json::Value {
120 serde_json::json!({
121 "status": "healthy",
122 "version": env!("CARGO_PKG_VERSION"),
123 "uptime_notice": "This is a demo application"
124 })
125}
126
127fn list_users(page: u64, per_page: u64) -> PaginatedResponse<User> {
131 let all_users = sample_users();
133 let total = all_users.len() as u64;
134 let total_pages = total.div_ceil(per_page);
135 #[allow(clippy::cast_possible_truncation)]
136 let start = ((page - 1) * per_page) as usize;
137 #[allow(clippy::cast_possible_truncation)]
138 let items: Vec<User> = all_users
139 .into_iter()
140 .skip(start)
141 .take(per_page as usize)
142 .collect();
143
144 PaginatedResponse {
145 items,
146 total,
147 page,
148 per_page,
149 total_pages,
150 }
151}
152
153fn get_user(id: u64) -> Result<ApiResponse<User>, (u16, String)> {
155 sample_users()
156 .into_iter()
157 .find(|u| u.id == id)
158 .map(ApiResponse::ok)
159 .ok_or((404, format!("User {id} not found")))
160}
161
162fn create_user(input: CreateUser) -> ApiResponse<User> {
164 let user = User {
165 id: 42, name: input.name,
167 email: input.email,
168 role: input.role.unwrap_or(UserRole::Viewer),
169 };
170 ApiResponse::with_message(user, "User created successfully")
171}
172
173#[allow(dead_code)]
175fn delete_user(id: u64) -> Result<ApiResponse<()>, (u16, String)> {
176 if sample_users().iter().any(|u| u.id == id) {
177 Ok(ApiResponse::with_message((), format!("User {id} deleted")))
178 } else {
179 Err((404, format!("User {id} not found")))
180 }
181}
182
183fn build_config() -> AppConfig {
189 AppConfig {
190 name: "Demo API".into(),
191 version: env!("CARGO_PKG_VERSION").into(),
192 debug: cfg!(debug_assertions),
193 max_body_size: 10 * 1024 * 1024, request_timeout_ms: 30_000,
195 root_path: String::new(),
196 root_path_in_servers: false,
197 trailing_slash_mode: fastapi_core::routing::TrailingSlashMode::Strict,
198 debug_config: fastapi_core::error::DebugConfig::default(),
199 }
200}
201
202fn print_demo_info() {
204 println!("=== FastAPI Rust Demo Application ===");
205 println!();
206 println!("This example demonstrates:");
207 println!(" - Route definitions (GET, POST, DELETE)");
208 println!(" - Path and query parameter extraction");
209 println!(" - JSON request/response handling");
210 println!(" - Middleware configuration (CORS, security headers)");
211 println!(" - Pagination patterns");
212 println!(" - Error handling");
213 println!(" - Application configuration");
214 println!();
215 println!("Endpoints:");
216 println!(" GET /health - Health check");
217 println!(" GET /api/v1/users - List users (paginated)");
218 println!(" GET /api/v1/users/:id - Get user by ID");
219 println!(" POST /api/v1/users - Create user");
220 println!(" DELETE /api/v1/users/:id - Delete user");
221 println!();
222 println!("Configuration:");
223 let config = build_config();
224 println!(" Name: {}", config.name);
225 println!(" Version: {}", config.version);
226 println!(" Debug: {}", config.debug);
227 println!(
228 " Max body size: {} MB",
229 config.max_body_size / (1024 * 1024)
230 );
231 println!(" Timeout: {}ms", config.request_timeout_ms);
232 println!();
233 println!("CORS: allow_any_origin=true, allow_credentials=true");
234 println!("Rate limit: 100 req/min (token bucket)");
235 println!("Security headers: X-Content-Type-Options, X-Frame-Options, Referrer-Policy");
236 println!();
237 println!("--- Demo data ---");
238 println!();
239
240 println!("Health check:");
242 let health = health_check();
243 print_pretty(&health);
244 println!();
245
246 println!("List users (page 1, 2 per page):");
247 let users = list_users(1, 2);
248 print_pretty(&users);
249 println!();
250
251 println!("Get user 1:");
252 match get_user(1) {
253 Ok(resp) => print_pretty(&resp),
254 Err((code, msg)) => println!(" Error {code}: {msg}"),
255 }
256 println!();
257
258 println!("Get user 999 (not found):");
259 match get_user(999) {
260 Ok(resp) => print_pretty(&resp),
261 Err((code, msg)) => println!(" Error {code}: {msg}"),
262 }
263 println!();
264
265 println!("Create user:");
266 let new_user = create_user(CreateUser {
267 name: "New User".into(),
268 email: "new@example.com".into(),
269 role: Some(UserRole::Editor),
270 });
271 print_pretty(&new_user);
272}
273
274fn print_pretty<T: Serialize>(value: &T) {
275 match serde_json::to_string_pretty(value) {
276 Ok(text) => println!(" {text}"),
277 Err(err) => println!(" <json error: {err}>"),
278 }
279}
280
281fn sample_users() -> Vec<User> {
286 vec![
287 User {
288 id: 1,
289 name: "Alice Johnson".into(),
290 email: "alice@example.com".into(),
291 role: UserRole::Admin,
292 },
293 User {
294 id: 2,
295 name: "Bob Smith".into(),
296 email: "bob@example.com".into(),
297 role: UserRole::Editor,
298 },
299 User {
300 id: 3,
301 name: "Carol Williams".into(),
302 email: "carol@example.com".into(),
303 role: UserRole::Viewer,
304 },
305 ]
306}
307
308fn main() {
313 print_demo_info();
314}