Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/custom_types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "custom_types"
version = "0.1.0"
edition = "2021"

[dependencies]
gotcha = { path = "../../gotcha" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing-subscriber = "0.3"
58 changes: 58 additions & 0 deletions examples/custom_types/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use gotcha::prelude::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};

// Custom application state
#[derive(Clone, Default)]
struct AppState {
request_counter: Arc<AtomicU64>,
}

// Custom application configuration
#[derive(Clone, Default, Serialize, Deserialize)]
struct AppConfig {
api_key: String,
max_connections: u32,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();

println!("🚀 Demonstrating new Gotcha API improvements");
println!();

// The NEW way - clean and simple!
println!("✨ NEW API - No more <(), ()> needed!");
println!(" Gotcha::with_types::<AppState, AppConfig>()");
println!(" Gotcha::with_state::<AppState>()");
println!(" Gotcha::with_config::<AppConfig>()");

// Example 1: Using with_types for both state and config
let _app1 = Gotcha::with_types::<AppState, AppConfig>()
.state(AppState::default())
.get("/", || async { "App with custom state and config" });

// Example 2: Using with_state for custom state only
let _app2 = Gotcha::with_state::<AppState>()
.state(AppState::default())
.get("/", || async { "App with custom state" });

// Example 3: Using with_config for custom config only
let _app3 = Gotcha::with_config::<AppConfig>()
.with_env_config("APP")
.get("/", || async { "App with custom config" });

// Example 4: Traditional approach for simple apps still works
let _app4 = Gotcha::new()
.get("/", || async { "Simple app" });

println!();
println!("✅ All API variations compile successfully!");
println!();
println!("💡 The old way would have required:");
println!(" Gotcha::<(), ()>::with_types::<AppState, AppConfig>() // 😕 Awkward!");

Ok(())
}
11 changes: 11 additions & 0 deletions examples/custom_types_real/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "custom_types_real"
version = "0.1.0"
edition = "2021"

[dependencies]
gotcha = { path = "../../gotcha" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing-subscriber = "0.3"
142 changes: 142 additions & 0 deletions examples/custom_types_real/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! Real-world example showing how the improved Gotcha API makes it easier
//! to work with custom state and configuration types.

use gotcha::prelude::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use tokio::sync::RwLock;
use std::collections::HashMap;

// Application state with a counter and a simple in-memory store
#[derive(Clone, Default)]
struct AppState {
request_counter: Arc<AtomicU64>,
users: Arc<RwLock<HashMap<u64, User>>>,
}

// Application configuration
#[derive(Clone, Serialize, Deserialize)]
struct AppConfig {
api_key: String,
max_users: usize,
}

impl Default for AppConfig {
fn default() -> Self {
Self {
api_key: "demo-key".to_string(),
max_users: 100,
}
}
}

// Domain model
#[derive(Clone, Serialize, Deserialize, Debug)]
struct User {
id: u64,
name: String,
email: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();

println!("🚀 Starting Real-World Gotcha Example with Custom Types");
println!();

// Initialize state with some sample data
let mut initial_users = HashMap::new();
initial_users.insert(1, User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
});
initial_users.insert(2, User {
id: 2,
name: "Bob".to_string(),
email: "bob@example.com".to_string(),
});

let state = AppState {
request_counter: Arc::new(AtomicU64::new(0)),
users: Arc::new(RwLock::new(initial_users)),
};

// ✨ The NEW clean API - No more Gotcha::<(), ()>::with_types!
let app = Gotcha::with_types::<AppState, AppConfig>()
.state(state)
.with_env_config("APP")

// Home route with request counter
.get("/", |ctx: State<GotchaContext<AppState, AppConfig>>| async move {
let count = ctx.state.request_counter.fetch_add(1, Ordering::SeqCst);
format!("Welcome to Gotcha! This is request #{}", count + 1)
})

// Get all users
.get("/users", |ctx: State<GotchaContext<AppState, AppConfig>>| async move {
let users = ctx.state.users.read().await;
let users_vec: Vec<&User> = users.values().collect();
Json(users_vec)
})

// Get user by ID
.get("/users/:id", |
Path(id): Path<u64>,
ctx: State<GotchaContext<AppState, AppConfig>>
| async move {
let users = ctx.state.users.read().await;
match users.get(&id) {
Some(user) => Ok(Json(user.clone())),
None => Err((StatusCode::NOT_FOUND, "User not found"))
}
})

// Create new user
.post("/users", || async {
Json(serde_json::json!({
"message": "User creation endpoint (simplified for demo)"
}))
})

// Show configuration
.get("/config", |ctx: State<GotchaContext<AppState, AppConfig>>| async move {
Json(serde_json::json!({
"api_key": if ctx.config.application.api_key.is_empty() {
"not-configured"
} else {
"***hidden***"
},
"max_users": ctx.config.application.max_users
}))
})

// Health check
.get("/health", || async {
Json(serde_json::json!({
"status": "healthy",
"service": "gotcha-example"
}))
});

println!("✅ API endpoints:");
println!(" GET / - Home with request counter");
println!(" GET /users - List all users");
println!(" GET /users/:id - Get user by ID");
println!(" POST /users - Create new user");
println!(" GET /config - Show configuration");
println!(" GET /health - Health check");
println!();
println!("🌐 Server running at http://127.0.0.1:3000");
println!();
println!("💡 Try:");
println!(" curl http://127.0.0.1:3000/users");
println!(" curl -X POST http://127.0.0.1:3000/users -H 'Content-Type: application/json' -d '{{\"name\":\"Charlie\",\"email\":\"charlie@example.com\"}}'");
println!();

app.listen("127.0.0.1:3000").await?;

Ok(())
}
11 changes: 11 additions & 0 deletions examples/custom_types_simple/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "custom_types_simple"
version = "0.1.0"
edition = "2021"

[dependencies]
gotcha = { path = "../../gotcha" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing-subscriber = "0.3"
70 changes: 70 additions & 0 deletions examples/custom_types_simple/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! Simplified example showcasing the new Gotcha API improvements

use gotcha::prelude::*;
use serde::{Deserialize, Serialize};

// Custom application state
#[derive(Clone, Default)]
struct MyState {
name: String,
}

// Custom application configuration
#[derive(Clone, Default, Serialize, Deserialize)]
struct MyConfig {
api_key: String,
port: u16,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();

println!("🚀 Gotcha API Improvements Demo");
println!("================================");
println!();

// ✨ The OLD way (before the improvement) would have required:
// Gotcha::<(), ()>::with_types::<MyState, MyConfig>() // 😕 Awkward!

// ✨ The NEW way - clean and intuitive:
println!("✅ NEW API - Direct and clean:");
println!();
println!(" // For custom state and config:");
println!(" Gotcha::with_types::<MyState, MyConfig>()");
println!();
println!(" // For custom state only:");
println!(" Gotcha::with_state::<MyState>()");
println!();
println!(" // For custom config only:");
println!(" Gotcha::with_config::<MyConfig>()");
println!();
println!(" // For simple apps (unchanged):");
println!(" Gotcha::new()");
println!();

// Test that all variations compile correctly
let _app1 = Gotcha::with_types::<MyState, MyConfig>()
.state(MyState { name: "Example".to_string() })
.get("/", || async { "Custom state and config" });

let _app2 = Gotcha::with_state::<MyState>()
.state(MyState { name: "Example".to_string() })
.get("/", || async { "Custom state only" });

let _app3 = Gotcha::with_config::<MyConfig>()
.get("/", || async { "Custom config only" });

let _app4 = Gotcha::new()
.get("/", || async { "Simple app" });

println!("✨ Benefits of the new API:");
println!(" • No more confusing <(), ()> type parameters");
println!(" • Cleaner, more intuitive syntax");
println!(" • Better developer experience");
println!(" • Same powerful functionality");
println!();
println!("✅ All API variations compile successfully!");

Ok(())
}
Loading
Loading