use std::time::Duration;
use http::Method;
use soaprs_core::{SoapError, SoapResult};
use crate::{AuthorizationPolicy, RateLimitPolicy, RoutePath};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EndpointMetadata {
pub name: String,
pub method: Method,
pub path: RoutePath,
pub authorization: AuthorizationPolicy,
pub rate_limit: Option<RateLimitPolicy>,
pub timeout: Option<Duration>,
pub tags: Vec<String>,
}
impl EndpointMetadata {
pub fn new(name: impl Into<String>, method: Method, path: RoutePath) -> SoapResult<Self> {
let name = name.into();
if name.is_empty()
|| !name.chars().all(|character| {
character == '.' || character == '_' || character.is_ascii_alphanumeric()
})
{
return Err(SoapError::validation(format!(
"invalid endpoint name `{name}`"
)));
}
Ok(Self {
name,
method,
path,
authorization: AuthorizationPolicy::Public,
rate_limit: None,
timeout: None,
tags: Vec::new(),
})
}
pub fn authorize(mut self, policy: AuthorizationPolicy) -> Self {
self.authorization = policy;
self
}
pub fn rate_limit(mut self, policy: RateLimitPolicy) -> Self {
self.rate_limit = Some(policy);
self
}
pub fn timeout(mut self, timeout: Duration) -> SoapResult<Self> {
if timeout.is_zero() {
return Err(SoapError::validation(
"endpoint timeout must be greater than zero",
));
}
self.timeout = Some(timeout);
Ok(self)
}
pub fn tag(mut self, tag: impl Into<String>) -> SoapResult<Self> {
let tag = tag.into();
if tag.trim().is_empty() {
return Err(SoapError::validation("endpoint tag cannot be empty"));
}
if !self.tags.contains(&tag) {
self.tags.push(tag);
}
Ok(self)
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use http::Method;
use crate::{AuthorizationPolicy, EndpointMetadata, RoutePath};
#[test]
fn builds_metadata_without_a_framework_handler() {
let result = EndpointMetadata::new(
"users.get",
Method::GET,
match RoutePath::new("/users/{id}") {
Ok(path) => path,
Err(error) => panic!("valid path failed: {error}"),
},
)
.map(|metadata| metadata.authorize(AuthorizationPolicy::Authenticated))
.and_then(|metadata| metadata.timeout(Duration::from_secs(5)))
.and_then(|metadata| metadata.tag("users"));
assert!(result.is_ok());
}
}