use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Serialize, Deserialize)]
pub enum ConfigStructure {
#[serde(alias = "single", alias = "SINGLE")]
Single(RouterConfig),
#[serde(alias = "shared", alias = "SHARED")]
Shared(SharedConfig),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum RouteType {
None,
#[serde(rename = "header", alias = "Header", alias = "HEADER")]
Header(String),
#[serde(rename = "path", alias = "Path", alias = "PATH")]
Path,
}
impl RouteType {
pub fn header(header_name: impl Into<String>) -> Self {
Self::Header(header_name.into())
}
pub fn path() -> Self {
Self::Path
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SharedConfig {
key: RouteType,
services: HashMap<String, RouterConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouterConfig {
pub(crate) handlers: HashSet<String>,
pub(crate) chains: HashMap<String, Vec<String>>,
pub(crate) routes: Routes,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Routes {
#[serde(rename = "path", alias = "Path", alias = "PATH")]
HttpRequestPaths(HashMap<String, HashMap<String, PathChain>>),
#[serde(rename = "header", alias = "Header", alias = "HEADER")]
HttpHeaderPaths(HashMap<String, HashMap<String, PathChain>>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathChain {
#[serde(skip_serializing_if = "Option::is_none", rename = "request")]
pub(crate) request_handlers: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "termination")]
pub(crate) termination_handler: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "response")]
pub(crate) response_handlers: Option<Vec<String>>,
}
impl PathChain {
pub fn new() -> Self {
Self {
request_handlers: None,
termination_handler: None,
response_handlers: None,
}
}
fn add_request_handler(&mut self, handler: impl Into<String>) -> &mut Self {
self.request_handlers
.get_or_insert_with(Vec::new)
.push(handler.into());
self
}
fn termination_handler(&mut self, handler: impl Into<String>) -> &mut Self {
self.termination_handler = Some(handler.into());
self
}
fn add_response_handler(&mut self, handler: impl Into<String>) -> &mut Self {
self.response_handlers
.get_or_insert_with(Vec::new)
.push(handler.into());
self
}
}
impl Default for PathChain {
fn default() -> Self {
Self::new()
}
}
pub mod builder {
use super::*;
pub struct ServiceConfigCore {
handlers: HashSet<String>,
chains: HashMap<String, Vec<String>>,
routes: HashMap<String, HashMap<String, PathChain>>,
}
impl Default for ServiceConfigCore {
fn default() -> Self {
Self::new()
}
}
impl ServiceConfigCore {
pub fn new() -> Self {
Self {
handlers: HashSet::new(),
chains: HashMap::new(),
routes: HashMap::new(),
}
}
pub fn add_handler(&mut self, handler_name: impl Into<String>) {
self.handlers.insert(handler_name.into());
}
pub fn add_handlers(&mut self, handler_names: &[impl AsRef<str>]) {
for name in handler_names {
self.handlers.insert(name.as_ref().to_string());
}
}
pub fn add_chain(
&mut self,
chain_name: impl Into<String>,
handler_names: &[impl AsRef<str>],
) {
let chain = handler_names
.iter()
.map(|name| name.as_ref().to_string())
.collect();
self.chains.insert(chain_name.into(), chain);
}
pub fn ensure_route_exists(&mut self, path: &str) {
self.routes.entry(path.to_string()).or_default();
}
pub fn add_method(&mut self, path: &str, method: String, path_chain: PathChain) {
self.routes
.entry(path.to_string())
.or_default()
.insert(method, path_chain);
}
pub fn build(self) -> RouterConfig {
RouterConfig {
handlers: self.handlers,
chains: self.chains,
routes: Routes::HttpRequestPaths(self.routes),
}
}
}
pub trait ServiceBuilder: Sized {
type RouteBuilder;
fn core(&mut self) -> &mut ServiceConfigCore;
fn handler(mut self, handler_name: impl Into<String>) -> Self {
self.core().add_handler(handler_name);
self
}
fn handlers(mut self, handler_names: &[impl AsRef<str>]) -> Self {
self.core().add_handlers(handler_names);
self
}
fn chain(
mut self,
chain_name: impl Into<String>,
handler_names: &[impl AsRef<str>],
) -> Self {
self.core().add_chain(chain_name, handler_names);
self
}
fn route(self, path: impl Into<String>) -> Self::RouteBuilder;
}
pub trait RouteBuilder: Sized {
type MethodBuilder;
type ServiceBuilder;
fn create_method_builder(self, method: impl Into<String>) -> Self::MethodBuilder;
fn end_route(self) -> Self::ServiceBuilder;
fn head(self) -> Self::MethodBuilder {
self.create_method_builder("HEAD")
}
fn options(self) -> Self::MethodBuilder {
self.create_method_builder("OPTIONS")
}
fn get(self) -> Self::MethodBuilder {
self.create_method_builder("GET")
}
fn post(self) -> Self::MethodBuilder {
self.create_method_builder("POST")
}
fn put(self) -> Self::MethodBuilder {
self.create_method_builder("PUT")
}
fn delete(self) -> Self::MethodBuilder {
self.create_method_builder("DELETE")
}
fn patch(self) -> Self::MethodBuilder {
self.create_method_builder("PATCH")
}
}
pub trait MethodBuilder: Sized {
type RouteBuilder;
fn path_chain(&mut self) -> &mut PathChain;
fn chains(&self) -> &HashMap<String, Vec<String>>;
fn request_handlers(mut self, handlers: &[impl AsRef<str>]) -> Self {
let path_chain = self.path_chain();
for handler in handlers {
path_chain.add_request_handler(handler.as_ref().to_string());
}
self
}
fn request_handler(mut self, handler: impl Into<String>) -> Self {
self.path_chain().add_request_handler(handler);
self
}
fn termination_handler(mut self, handler: impl Into<String>) -> Self {
self.path_chain().termination_handler(handler);
self
}
fn response_handlers(mut self, handlers: &[impl AsRef<str>]) -> Self {
let path_chain = self.path_chain();
for handler in handlers {
path_chain.add_response_handler(handler.as_ref().to_string());
}
self
}
fn response_handler(mut self, handler: impl Into<String>) -> Self {
self.path_chain().add_response_handler(handler);
self
}
fn request_chain(mut self, chain_name: impl AsRef<str>) -> Self {
if let Some(chain_handlers) = self.chains().get(chain_name.as_ref()) {
let handlers: Vec<String> = chain_handlers.clone();
for handler in handlers {
self.path_chain().add_request_handler(handler);
}
}
self
}
fn response_chain(mut self, chain_name: impl AsRef<str>) -> Self {
if let Some(chain_handlers) = self.chains().get(chain_name.as_ref()) {
let handlers: Vec<String> = chain_handlers.clone();
for handler in handlers {
self.path_chain().add_response_handler(handler);
}
}
self
}
fn end_method(self) -> Self::RouteBuilder;
}
pub struct SingleServiceConfigBuilder {
core: ServiceConfigCore,
}
impl Default for SingleServiceConfigBuilder {
fn default() -> Self {
Self::new()
}
}
impl SingleServiceConfigBuilder {
pub fn new() -> Self {
Self {
core: ServiceConfigCore::new(),
}
}
pub fn build(self) -> RouterConfig {
self.core.build()
}
}
impl ServiceBuilder for SingleServiceConfigBuilder {
type RouteBuilder = SingleServiceRouteBuilder;
fn core(&mut self) -> &mut ServiceConfigCore {
&mut self.core
}
fn route(mut self, path: impl Into<String>) -> Self::RouteBuilder {
let path_str = path.into();
self.core.ensure_route_exists(&path_str);
SingleServiceRouteBuilder {
config_builder: self,
current_path: path_str,
}
}
}
pub struct SingleServiceRouteBuilder {
config_builder: SingleServiceConfigBuilder,
current_path: String,
}
impl RouteBuilder for SingleServiceRouteBuilder {
type MethodBuilder = SingleServiceMethodBuilder;
type ServiceBuilder = SingleServiceConfigBuilder;
fn create_method_builder(self, method: impl Into<String>) -> Self::MethodBuilder {
SingleServiceMethodBuilder {
route_builder: self,
method: method.into(),
path_chain: PathChain::new(),
}
}
fn end_route(self) -> Self::ServiceBuilder {
self.config_builder
}
}
pub struct SingleServiceMethodBuilder {
route_builder: SingleServiceRouteBuilder,
method: String,
path_chain: PathChain,
}
impl MethodBuilder for SingleServiceMethodBuilder {
type RouteBuilder = SingleServiceRouteBuilder;
fn path_chain(&mut self) -> &mut PathChain {
&mut self.path_chain
}
fn chains(&self) -> &HashMap<String, Vec<String>> {
&self.route_builder.config_builder.core.chains
}
fn end_method(self) -> Self::RouteBuilder {
let mut route_builder = self.route_builder;
route_builder.config_builder.core.add_method(
&route_builder.current_path,
self.method,
self.path_chain,
);
route_builder
}
}
pub struct SharedConfigBuilder {
key: RouteType,
services: HashMap<String, RouterConfig>,
}
impl SharedConfigBuilder {
pub fn new() -> Self {
Self {
key: RouteType::None,
services: HashMap::new(),
}
}
pub fn route_type(mut self, route_type: RouteType) -> Self {
self.key = route_type;
self
}
pub fn service(self, service_name: impl Into<String>) -> SharedServiceBuilder {
SharedServiceBuilder {
shared_builder: self,
service_name: service_name.into(),
core: ServiceConfigCore::new(),
}
}
pub fn build(self) -> SharedConfig {
SharedConfig {
key: self.key,
services: self.services,
}
}
}
pub struct SharedServiceBuilder {
shared_builder: SharedConfigBuilder,
service_name: String,
core: ServiceConfigCore,
}
impl SharedServiceBuilder {
pub fn end_service(mut self) -> SharedConfigBuilder {
let config = self.core.build();
self.shared_builder
.services
.insert(self.service_name, config);
self.shared_builder
}
}
impl ServiceBuilder for SharedServiceBuilder {
type RouteBuilder = SharedServiceRouteBuilder;
fn core(&mut self) -> &mut ServiceConfigCore {
&mut self.core
}
fn route(mut self, path: impl Into<String>) -> Self::RouteBuilder {
let path_str = path.into();
self.core.ensure_route_exists(&path_str);
SharedServiceRouteBuilder {
service_builder: self,
current_path: path_str,
}
}
}
pub struct SharedServiceRouteBuilder {
service_builder: SharedServiceBuilder,
current_path: String,
}
impl RouteBuilder for SharedServiceRouteBuilder {
type MethodBuilder = SharedServiceMethodBuilder;
type ServiceBuilder = SharedServiceBuilder;
fn create_method_builder(self, method: impl Into<String>) -> Self::MethodBuilder {
SharedServiceMethodBuilder {
route_builder: self,
method: method.into(),
path_chain: PathChain::new(),
}
}
fn end_route(self) -> Self::ServiceBuilder {
self.service_builder
}
}
pub struct SharedServiceMethodBuilder {
route_builder: SharedServiceRouteBuilder,
method: String,
path_chain: PathChain,
}
impl MethodBuilder for SharedServiceMethodBuilder {
type RouteBuilder = SharedServiceRouteBuilder;
fn path_chain(&mut self) -> &mut PathChain {
&mut self.path_chain
}
fn chains(&self) -> &HashMap<String, Vec<String>> {
&self.route_builder.service_builder.core.chains
}
fn end_method(self) -> Self::RouteBuilder {
let mut route_builder = self.route_builder;
route_builder.service_builder.core.add_method(
&route_builder.current_path,
self.method,
self.path_chain,
);
route_builder
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[rustfmt::skip]
fn test_single_service_multiple_methods_same_route() {
let config = SingleServiceConfigBuilder::new()
.handler("handler1")
.handler("handler2")
.route("/api/test")
.get()
.request_handler("handler1")
.termination_handler("handler2")
.end_method()
.post()
.request_handler("handler1")
.termination_handler("handler2")
.end_method()
.end_route()
.build();
assert!(config.handlers.contains("handler1"));
assert!(config.handlers.contains("handler2"));
}
#[test]
#[rustfmt::skip]
fn test_shared_service_multiple_methods_same_route() {
let config = SharedConfigBuilder::new()
.route_type(RouteType::path())
.service("service1")
.handler("handler1")
.route("/api/test")
.get()
.termination_handler("handler1")
.end_method()
.end_route()
.end_service()
.build();
assert!(config.services.contains_key("service1"));
}
#[test]
#[rustfmt::skip]
fn test_route_builder_chaining() {
let config = SingleServiceConfigBuilder::new()
.handlers(&["auth", "validate", "process", "respond"])
.chain("request_chain", &["auth", "validate"])
.chain("response_chain", &["respond"])
.route("/api/users")
.get()
.request_chain("request_chain")
.termination_handler("process")
.response_chain("response_chain")
.end_method()
.post()
.request_handlers(&["auth", "validate"])
.termination_handler("process")
.response_handler("respond")
.end_method()
.end_route()
.build();
assert_eq!(config.handlers.len(), 4);
assert_eq!(config.chains.len(), 2);
}
}
}
#[cfg(test)]
mod test {
use super::*;
use serde_json;
#[test]
fn load_shared_config() {
let json_config = r#"
{
"shared": {
"key": "path",
"services": {
"api_v1": {
"handlers": ["auth", "handler1"],
"chains": {},
"routes": {
"path": {
"/users": {
"GET": {
"request": ["auth"],
"termination": "handler1"
}
}
}
}
}
}
}
}
"#;
let config: ConfigStructure = serde_json::from_str(json_config).unwrap();
match config {
ConfigStructure::Shared(shared) => {
assert_eq!(shared.key, RouteType::Path);
assert!(shared.services.contains_key("api_v1"));
}
_ => panic!("Expected shared configuration"),
}
}
#[test]
fn load_single_config() {
let json_config = r#"
{
"single": {
"handlers": ["handler1", "handler2"],
"chains": {
"auth_chain": ["handler1", "handler2"]
},
"routes": {
"path": {
"/test": {
"GET": {
"termination": "handler1"
}
}
}
}
}
}
"#;
let config: ConfigStructure = serde_json::from_str(json_config).unwrap();
match config {
ConfigStructure::Single(single) => {
assert!(single.handlers.contains("handler1"));
assert!(single.handlers.contains("handler2"));
assert!(single.chains.contains_key("auth_chain"));
}
_ => panic!("Expected single configuration"),
}
}
}