kegani 0.1.1

A developer-friendly, ergonomic, production-ready Rust web framework
Documentation
//! Route registration for Kegani
//!
//! Provides route configuration types.

use std::collections::HashMap;
use once_cell::sync::Lazy;
use std::sync::Mutex;

/// Global route registry
pub static ROUTE_REGISTRY: Lazy<Mutex<HashMap<String, RouteConfig>>> =
    Lazy::new(|| Mutex::new(HashMap::new()));

/// Route configuration
#[derive(Debug, Clone)]
pub struct RouteConfig {
    pub path: String,
    pub method: String,
    pub handler_name: String,
}

/// Route registration helper
pub struct Route;

impl Route {
    /// Register a handler function
    pub fn register(name: &str, path: &str, method: &str) {
        let config = RouteConfig {
            path: path.to_string(),
            method: method.to_string(),
            handler_name: name.to_string(),
        };

        let key = format!("{}:{}", method, path);
        if let Ok(mut registry) = ROUTE_REGISTRY.lock() {
            registry.insert(key, config);
        }

        tracing::debug!("Registered route: {} {} -> {}", method, path, name);
    }

    /// Get all registered routes
    pub fn all_routes() -> Vec<RouteConfig> {
        ROUTE_REGISTRY
            .lock()
            .map(|r| r.values().cloned().collect())
            .unwrap_or_default()
    }

    /// Get routes by path pattern
    pub fn find_routes(path: &str) -> Vec<RouteConfig> {
        ROUTE_REGISTRY
            .lock()
            .map(|r| {
                r.values()
                    .filter(|c| c.path == path)
                    .cloned()
                    .collect()
            })
            .unwrap_or_default()
    }

    /// Get routes by HTTP method
    pub fn by_method(method: &str) -> Vec<RouteConfig> {
        ROUTE_REGISTRY
            .lock()
            .map(|r| {
                r.values()
                    .filter(|c| c.method.eq_ignore_ascii_case(method))
                    .cloned()
                    .collect()
            })
            .unwrap_or_default()
    }

    /// Clear all routes (useful for testing)
    #[cfg(test)]
    pub fn clear() {
        if let Ok(mut r) = ROUTE_REGISTRY.lock() {
            r.clear();
        }
    }
}

/// Route scope for grouping routes
pub struct RouteScope {
    prefix: String,
}

impl RouteScope {
    /// Create a new route scope with path prefix
    pub fn new(prefix: &str) -> Self {
        Self {
            prefix: prefix.trim_end_matches('/').to_string(),
        }
    }

    /// Join scope prefix with route path
    pub fn join(&self, path: &str) -> String {
        if path.starts_with('/') {
            format!("{}{}", self.prefix, path)
        } else {
            format!("{}/{}", self.prefix, path)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_route_register() {
        Route::clear();
        Route::register("handler1", "/api/users", "GET");
        Route::register("handler2", "/api/users", "POST");

        let routes = Route::all_routes();
        assert_eq!(routes.len(), 2);
    }

    #[test]
    fn test_route_scope() {
        let scope = RouteScope::new("/api/v1");
        assert_eq!(scope.join("/users"), "/api/v1/users");
        assert_eq!(scope.join("users"), "/api/v1/users");
    }
}