use pixeluvw_supabase::{Middleware, RequestContext, SupabaseClient};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
pub struct LoggingMiddleware {
prefix: String,
}
impl LoggingMiddleware {
pub fn new(prefix: &str) -> Self {
Self {
prefix: prefix.to_string(),
}
}
}
impl Middleware for LoggingMiddleware {
fn on_request(&self, ctx: &RequestContext) {
println!("[{}] → {} {}", self.prefix, ctx.method, ctx.table);
}
fn on_response(&self, ctx: &RequestContext, status: u16, duration_ms: u64) {
println!(
"[{}] ← {} {} -> {} ({}ms)",
self.prefix, ctx.method, ctx.table, status, duration_ms
);
}
fn on_error(&self, ctx: &RequestContext, error: &str) {
eprintln!(
"[{}] ✗ {} {}: {}",
self.prefix, ctx.method, ctx.table, error
);
}
}
pub struct MetricsMiddleware {
total_requests: AtomicU64,
total_errors: AtomicU64,
total_latency_ms: AtomicU64,
}
impl Default for MetricsMiddleware {
fn default() -> Self {
Self::new()
}
}
impl MetricsMiddleware {
pub fn new() -> Self {
Self {
total_requests: AtomicU64::new(0),
total_errors: AtomicU64::new(0),
total_latency_ms: AtomicU64::new(0),
}
}
pub fn get_stats(&self) -> (u64, u64, u64) {
(
self.total_requests.load(Ordering::Relaxed),
self.total_errors.load(Ordering::Relaxed),
self.total_latency_ms.load(Ordering::Relaxed),
)
}
pub fn avg_latency_ms(&self) -> f64 {
let requests = self.total_requests.load(Ordering::Relaxed);
if requests == 0 {
return 0.0;
}
self.total_latency_ms.load(Ordering::Relaxed) as f64 / requests as f64
}
}
impl Middleware for MetricsMiddleware {
fn on_request(&self, _ctx: &RequestContext) {
self.total_requests.fetch_add(1, Ordering::Relaxed);
}
fn on_response(&self, _ctx: &RequestContext, _status: u16, duration_ms: u64) {
self.total_latency_ms
.fetch_add(duration_ms, Ordering::Relaxed);
}
fn on_error(&self, _ctx: &RequestContext, _error: &str) {
self.total_errors.fetch_add(1, Ordering::Relaxed);
}
}
pub struct RateLimitTracker {
rate_limited_count: AtomicU64,
}
impl Default for RateLimitTracker {
fn default() -> Self {
Self::new()
}
}
impl RateLimitTracker {
pub fn new() -> Self {
Self {
rate_limited_count: AtomicU64::new(0),
}
}
pub fn get_rate_limited_count(&self) -> u64 {
self.rate_limited_count.load(Ordering::Relaxed)
}
}
impl Middleware for RateLimitTracker {
fn on_response(&self, _ctx: &RequestContext, status: u16, _duration_ms: u64) {
if status == 429 {
self.rate_limited_count.fetch_add(1, Ordering::Relaxed);
eprintln!("⚠️ Rate limited! Total: {}", self.get_rate_limited_count());
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🔌 Middleware Examples\n");
println!(" Note: Ensure .env is set with SUPABASE_URL and SUPABASE_KEY\n");
let metrics = Arc::new(MetricsMiddleware::new());
let client = SupabaseClient::from_env()?
.with_middleware(Arc::new(LoggingMiddleware::new("SUPABASE")))
.with_middleware(metrics.clone())
.with_middleware(Arc::new(RateLimitTracker::new()));
println!("Client created with 3 middlewares attached.\n");
println!("Making test query...\n");
let _: Result<Vec<serde_json::Value>, _> =
client.from("test").select("*").limit(1).execute().await;
let (requests, errors, _latency) = metrics.get_stats();
println!("\n📊 Metrics Summary:");
println!(" Total requests: {}", requests);
println!(" Total errors: {}", errors);
println!(" Avg latency: {:.2}ms", metrics.avg_latency_ms());
Ok(())
}