use axum::response::Response; use lazy_static::lazy_static; use tokio::sync::Mutex; use crate::{ api::{admin, auth, posts, users}, types::http::ResponseCode, }; use super::console::beautify; pub enum EndpointMethod { Post, Put, Patch, } impl ToString for EndpointMethod { fn to_string(&self) -> String { match self { Self::Post => "POST".to_owned(), Self::Put => "PUT".to_owned(), Self::Patch => "PATCH".to_owned(), } } } pub struct EndpointDocumentation<'a> { pub uri: &'static str, pub method: EndpointMethod, pub description: &'static str, pub body: Option<&'static str>, pub responses: &'a [(u16, &'static str)], pub cookie: Option<&'static str>, } lazy_static! { static ref ENDPOINTS: Mutex> = Mutex::new(Vec::new()); } fn generate_body(body: Option<&'static str>) -> String { let Some(body) = body else { return String::new() }; let html = r#"

Body

$body
"# .to_string(); let body = body.trim(); if body.starts_with('{') { return html.replace( "$body", &beautify(body) .replace("}"), ); } html.replace("$body", body) } fn generate_responses(responses: &[(u16, &'static str)]) -> String { let mut html = r#"

Responses

$responses "# .to_string(); for response in responses { let res = format!( r#"
{} {}
$responses "#, response.0, response.1 ); html = html.replace("$responses", &res); } html.replace("$responses", "") } fn generate_cookie(cookie: Option<&'static str>) -> String { let Some(cookie) = cookie else { return String::new() }; format!( r#"{cookie} cookie is required for authentication"# ) } fn generate_endpoint(doc: &EndpointDocumentation) -> String { let html = r#"
$method $uri $description $cookie
$body $responses
"#; html.replace("$method_class", &doc.method.to_string().to_lowercase()) .replace("$method", &doc.method.to_string()) .replace("$uri", doc.uri) .replace("$description", doc.description) .replace("$cookie", &generate_cookie(doc.cookie)) .replace("$body", &generate_body(doc.body)) .replace("$responses", &generate_responses(doc.responses)) } pub async fn init() { let docs = vec![ auth::AUTH_REGISTER, auth::AUTH_LOGIN, auth::AUTH_LOGOUT, posts::POSTS_CREATE, posts::POSTS_PAGE, posts::COMMENTS_PAGE, posts::POSTS_USER, posts::POSTS_COMMENT, posts::POSTS_LIKE, users::USERS_LOAD, users::USERS_PAGE, users::USERS_SELF, users::USERS_AVATAR, users::USERS_BANNER, users::USERS_FOLLOW, users::USERS_FOLLOW_STATUS, users::USERS_FRIENDS, admin::ADMIN_AUTH, admin::ADMIN_QUERY, admin::ADMIN_POSTS, admin::ADMIN_USERS, admin::ADMIN_SESSIONS, admin::ADMIN_COMMENTS, admin::ADMIN_LIKES, ]; let mut endpoints = ENDPOINTS.lock().await; for doc in docs { endpoints.push(generate_endpoint(&doc)); } } pub async fn generate() -> Response { let mut data = String::new(); { let endpoints = ENDPOINTS.lock().await; endpoints.iter().for_each(|endpoint| { data.push_str(endpoint); }); } let html = format!( r#" XSSBook - API Documentation
{data}
"# ); ResponseCode::Success.html(&html) }