use std::collections::HashMap;
pub const fn route_id(name: &str) -> u32 {
let bytes = name.as_bytes();
let mut hash: u32 = 0x811c9dc5; let mut i = 0;
while i < bytes.len() && i < 32 {
hash ^= bytes[i] as u32;
hash = hash.wrapping_mul(0x01000193); i += 1;
}
hash
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RouteCategory {
Basic,
PubSub,
Kv,
File,
Time,
Health,
Query,
Crypto,
Auth,
Terminal,
Other(&'static str),
}
impl RouteCategory {
pub fn name(self) -> &'static str {
match self {
Self::Basic => "Basic",
Self::PubSub => "PubSub",
Self::Kv => "KV Store",
Self::File => "File Operations",
Self::Time => "Time",
Self::Health => "Health",
Self::Query => "Query",
Self::Crypto => "Crypto",
Self::Auth => "Auth",
Self::Terminal => "Terminal",
Self::Other(name) => name,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Priority {
Background,
Low,
#[default]
Normal,
High,
Critical,
}
impl Priority {
pub fn as_i32(self) -> i32 {
match self {
Self::Background => -10,
Self::Low => -5,
Self::Normal => 0,
Self::High => 5,
Self::Critical => 10,
}
}
}
#[derive(Debug, Clone)]
pub struct RouteInfo {
pub id: u32,
pub name: &'static str,
pub category: RouteCategory,
pub priority: Priority,
pub requires_auth: bool,
pub description: Option<&'static str>,
}
#[derive(Debug, Default, Clone)]
pub struct RouteRegistry {
routes: HashMap<u32, RouteInfo>,
}
impl RouteRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(
&mut self,
name: &'static str,
category: RouteCategory,
priority: Priority,
requires_auth: bool,
description: Option<&'static str>,
) {
let id: u32 = route_id(name);
if let Some(existing) = self.routes.get(&id) {
panic!(
"route id collision in registry: \"{}\" and \"{}\" both hash to byte {} \
— rename one of them",
existing.name, name, id
);
}
self.routes.insert(
id,
RouteInfo { id, name, category, priority, requires_auth, description },
);
}
pub fn get(&self, id: u32) -> Option<&RouteInfo> {
self.routes.get(&id)
}
pub fn get_by_name(&self, name: &str) -> Option<&RouteInfo> {
self.routes.get(&route_id(name))
}
pub fn iter(&self) -> impl Iterator<Item = &RouteInfo> {
self.routes.values()
}
pub fn by_category(&self, category: RouteCategory) -> Vec<&RouteInfo> {
self.routes
.values()
.filter(|info| info.category == category)
.collect()
}
pub fn len(&self) -> usize {
self.routes.len()
}
pub fn is_empty(&self) -> bool {
self.routes.is_empty()
}
}
pub fn register_core_routes(registry: &mut RouteRegistry) {
registry.register(
"echo",
RouteCategory::Basic,
Priority::Normal,
false,
Some("Echo back the request"),
);
registry.register(
"uppercase",
RouteCategory::Basic,
Priority::Normal,
false,
Some("Convert text to uppercase"),
);
registry.register(
"reverse",
RouteCategory::Basic,
Priority::Normal,
false,
Some("Reverse the input string"),
);
registry.register(
"publish",
RouteCategory::PubSub,
Priority::Normal,
true,
Some("Publish a message to a topic"),
);
registry.register(
"subscribe",
RouteCategory::PubSub,
Priority::Normal,
true,
Some("Subscribe to a topic"),
);
registry.register(
"kv_set",
RouteCategory::Kv,
Priority::Normal,
true,
Some("Set a key-value pair"),
);
registry.register(
"kv_get",
RouteCategory::Kv,
Priority::Normal,
true,
Some("Get a value by key"),
);
registry.register(
"kv_delete",
RouteCategory::Kv,
Priority::Normal,
true,
Some("Delete a key-value pair"),
);
registry.register(
"file_upload",
RouteCategory::File,
Priority::Low,
false,
Some("Upload a file"),
);
registry.register(
"file_download",
RouteCategory::File,
Priority::Low,
false,
Some("Download a file"),
);
registry.register(
"file_list",
RouteCategory::File,
Priority::Low,
false,
Some("List files in directory"),
);
registry.register(
"file_delete",
RouteCategory::File,
Priority::Low,
false,
Some("Delete a file"),
);
registry.register(
"file_info",
RouteCategory::File,
Priority::Low,
false,
Some("Get file metadata"),
);
registry.register(
"time",
RouteCategory::Time,
Priority::Normal,
false,
Some("Get current time"),
);
registry.register(
"timestamp",
RouteCategory::Time,
Priority::Normal,
false,
Some("Get current timestamp"),
);
registry.register(
"health",
RouteCategory::Health,
Priority::High,
false,
Some("Health check"),
);
registry.register(
"status",
RouteCategory::Health,
Priority::High,
false,
Some("Detailed status"),
);
registry.register(
"query_json",
RouteCategory::Query,
Priority::Normal,
true,
Some("Execute a JSON query"),
);
registry.register(
"query_filtered",
RouteCategory::Query,
Priority::Normal,
true,
Some("Execute a filtered query"),
);
registry.register(
"query",
RouteCategory::Query,
Priority::Normal,
true,
Some("Execute a query"),
);
registry.register(
"hash",
RouteCategory::Crypto,
Priority::Normal,
true,
Some("Hash a value"),
);
registry.register(
"hmac",
RouteCategory::Crypto,
Priority::Normal,
true,
Some("HMAC a value"),
);
registry.register(
"auth",
RouteCategory::Auth,
Priority::Normal,
false,
Some("Authenticate a session"),
);
registry.register(
"whoami",
RouteCategory::Auth,
Priority::Normal,
true,
Some("Get current user info"),
);
registry.register(
"logout",
RouteCategory::Auth,
Priority::Normal,
true,
Some("Log out and invalidate the current session"),
);
registry.register(
"refresh",
RouteCategory::Auth,
Priority::Normal,
true,
Some("Refresh the current session's expiry"),
);
registry.register(
"admin_stats",
RouteCategory::Auth,
Priority::Normal,
true,
Some("Get admin statistics"),
);
registry.register(
"term_attach",
RouteCategory::Terminal,
Priority::Normal,
true,
Some("Attach to a terminal session"),
);
registry.register(
"term_resize",
RouteCategory::Terminal,
Priority::Normal,
true,
Some("Resize terminal window"),
);
}