use flowglad::types::customer::CreateCustomer;
use flowglad::{Client, Config, Error};
use std::env;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("⚠️ FlowGlad Error Handling Example\n");
println!("1️⃣ Testing invalid API key...");
let bad_config = Config::new("invalid_key");
match Client::new(bad_config) {
Ok(_) => {
println!(" ⚠️ Client created (validation happens at request time)");
}
Err(e) => {
println!(" ✓ Caught configuration error: {}", e);
}
}
println!();
println!("2️⃣ Testing API key with invalid characters...");
let invalid_config = Config::new("invalid\nkey\rwith\tcontrol\x00chars");
match Client::new(invalid_config) {
Ok(_) => {
println!(" ⚠️ Client created unexpectedly");
}
Err(e) => {
println!(" ✓ Caught configuration error: {}", e);
match e {
Error::Config(msg) => println!(" Type: Configuration error - {}", msg),
_ => println!(" Type: {}", e),
}
}
}
println!();
let api_key = match env::var("FLOWGLAD_API_KEY") {
Ok(key) => key,
Err(_) => {
println!("⚠️ FLOWGLAD_API_KEY not set. Remaining examples require a valid API key.");
println!(" Set it with: export FLOWGLAD_API_KEY=sk_test_...");
return Ok(());
}
};
let config = Config::new(api_key);
let client = Client::new(config)?;
println!("3️⃣ Testing 404 Not Found error...");
match client.customers().get("nonexistent_customer_12345").await {
Ok(_) => {
println!(" ⚠️ Unexpectedly found customer");
}
Err(e) => {
println!(" ✓ Caught API error: {}", e);
match e {
Error::Api {
status,
message,
code,
} => {
println!(" Status: {}", status);
println!(" Message: {}", message);
if let Some(code) = code {
println!(" Code: {}", code);
}
}
_ => println!(" Type: {}", e),
}
}
}
println!();
println!("4️⃣ Testing validation error...");
println!(" ✓ SDK prevents invalid requests at compile time");
println!(" Example: CreateCustomer::new() requires external_id and name");
println!();
println!("5️⃣ Testing custom timeout...");
let short_timeout_config = Config::builder()
.api_key(env::var("FLOWGLAD_API_KEY")?)
.timeout(Duration::from_millis(1)) .build()?;
let timeout_client = Client::new(short_timeout_config)?;
match timeout_client.customers().list().await {
Ok(_) => {
println!(" ⚠️ Request succeeded (network is very fast!)");
}
Err(e) => {
println!(" ✓ Caught timeout/network error: {}", e);
match e {
Error::Network(net_err) => {
if net_err.is_timeout() {
println!(" Type: Timeout error");
} else if net_err.is_connect() {
println!(" Type: Connection error");
} else {
println!(" Type: Network error - {}", net_err);
}
}
_ => println!(" Type: {}", e),
}
}
}
println!();
println!("6️⃣ Automatic retry behavior...");
println!(" ℹ️ The SDK automatically retries on:");
println!(" - 500 Internal Server Error");
println!(" - 502 Bad Gateway");
println!(" - 503 Service Unavailable");
println!(" - 504 Gateway Timeout");
println!(" ℹ️ Uses exponential backoff (100ms, 200ms, 400ms, ...)");
println!(" ℹ️ Configurable with Config::max_retries()");
println!();
println!("7️⃣ Rate limiting handling...");
println!(" ℹ️ When rate limited (429), the SDK returns:");
println!(" Error::RateLimit {{ retry_after: Option<Duration> }}");
println!(" ℹ️ The retry_after duration comes from the Retry-After header");
println!();
println!("8️⃣ Practical error handling pattern...");
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();
let external_id = format!("error_example_{}", timestamp);
let result = client
.customers()
.create(
CreateCustomer::new(&external_id, "Test User")
.email(&format!("test+{}@example.com", timestamp)),
)
.await;
match result {
Ok(customer) => {
println!(" ✓ Successfully created customer: {}", customer.id);
}
Err(Error::Api {
status,
message,
code,
}) => {
eprintln!(" ✗ API error ({}): {}", status, message);
if let Some(code) = code {
eprintln!(" Error code: {}", code);
}
match status {
400 => eprintln!(" Fix: Check your request parameters"),
401 => eprintln!(" Fix: Check your API key"),
404 => eprintln!(" Fix: Resource not found"),
429 => eprintln!(" Fix: Slow down your requests"),
_ => eprintln!(" Fix: Check FlowGlad status or try again"),
}
}
Err(Error::Network(e)) => {
eprintln!(" ✗ Network error: {}", e);
eprintln!(" Fix: Check your internet connection");
}
Err(Error::RateLimit { retry_after }) => {
eprintln!(" ✗ Rate limited!");
if let Some(duration) = retry_after {
eprintln!(" Retry after: {:?}", duration);
}
}
Err(Error::Serialization(e)) => {
eprintln!(" ✗ Serialization error: {}", e);
eprintln!(" This might indicate an API change or SDK bug");
}
Err(Error::Config(e)) => {
eprintln!(" ✗ Configuration error: {}", e);
eprintln!(" Fix: Check your Config setup");
}
Err(Error::Unknown(e)) => {
eprintln!(" ✗ Unknown error: {}", e);
}
Err(e) => {
eprintln!(" ✗ Unexpected error: {}", e);
}
}
println!("\n✨ Error handling example completed!");
println!("\n💡 Best Practices:");
println!(" - Always match on Error variants for proper handling");
println!(" - Use the Display trait (e.g., `{{}}`) for user-friendly messages");
println!(" - Use the Debug trait (e.g., `{{:?}}`) for detailed error info");
println!(" - Configure retries and timeouts based on your use case");
println!(" - Log errors for debugging but show friendly messages to users");
Ok(())
}