mod method_filter;
mod method_routing;
use std::convert::Infallible;
pub use method_filter::*;
pub use method_routing::*;
use indexmap::IndexMap;
use once_cell::sync::Lazy;
use regex::Regex;
use crate::{
paths::{OperationType, Paths},
Components, DefinitionHolder,
};
pub struct Router<S = ()> {
components: Components,
paths: Paths,
inner: axum::Router<S>,
}
impl<S> Router<S>
where
S: Clone + Send + Sync + 'static,
{
pub fn new() -> Self {
Self {
components: Components::default(),
paths: Default::default(),
inner: axum::Router::new(),
}
}
pub fn route(self, path: &str, method_router: MethodRouter<S>) -> Self {
self.inner = self.inner.route(path, method_router.inner);
}
fn update_from_def_holder(&mut self, definition_holder: &mut impl DefinitionHolder) {
let components = definition_holder
.components()
.into_iter()
.reduce(|mut acc, component| {
acc.merge(component);
acc
});
definition_holder.update_path_items(&mut self.paths.paths);
let mut paths = IndexMap::new();
for (path, mut item) in std::mem::take(&mut self.paths.paths) {
let path = if path.starts_with('/') {
path
} else {
"/".to_owned() + &path
};
item.operations.iter_mut().for_each(|(op_type, op)| {
let operation_id = build_operation_id(&path, op_type);
op.operation_id = op.operation_id.clone().or(Some(operation_id));
});
paths.insert(path, item);
}
self.paths.paths = paths;
if let Some(components) = components {
self.components.merge(components);
}
}
}
static PATH_RESOURCE_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"/(.*?)/\{(.*?)\}").expect("path template regex"));
fn build_operation_id(path: &str, operation_type: &OperationType) -> String {
let resource = PATH_RESOURCE_REGEX
.captures(path)
.and_then(|c| c.get(1))
.map(|_match| _match.as_str())
.unwrap_or(path)
.trim_matches('/');
format!(
"{:?}_{}-{:x}",
operation_type,
resource.replace('/', "-"),
md5::compute(path)
)
.to_lowercase()
}