miko 0.9.0

A web framework for Rust
Documentation
#![cfg(all(feature = "utoipa", feature = "auto"))]

use utoipa::Modify;

use crate::utoipa;

pub struct OpenApiRoute {
    pub openapi: fn() -> utoipa::openapi::OpenApi,
    pub module_path: &'static str,
}

inventory::collect!(OpenApiRoute);

pub struct AutoPaths;

impl Modify for AutoPaths {
    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
        for route in inventory::iter::<OpenApiRoute> {
            let mut route_openapi = (route.openapi)();
            apply_default_tag(&mut route_openapi, route.module_path);
            openapi.merge(route_openapi);
        }
    }
}

fn apply_default_tag(openapi: &mut utoipa::openapi::OpenApi, module_path: &str) {
    if module_path.is_empty() {
        return;
    }

    for path_item in openapi.paths.paths.values_mut() {
        set_default_tag(path_item.get.as_mut(), module_path);
        set_default_tag(path_item.put.as_mut(), module_path);
        set_default_tag(path_item.post.as_mut(), module_path);
        set_default_tag(path_item.delete.as_mut(), module_path);
        set_default_tag(path_item.options.as_mut(), module_path);
        set_default_tag(path_item.head.as_mut(), module_path);
        set_default_tag(path_item.patch.as_mut(), module_path);
        set_default_tag(path_item.trace.as_mut(), module_path);
    }
}

fn set_default_tag(operation: Option<&mut utoipa::openapi::path::Operation>, module_path: &str) {
    let Some(operation) = operation else {
        return;
    };

    if operation.tags.as_ref().is_none_or(Vec::is_empty) {
        operation.tags = Some(vec![module_path.to_string()]);
    }
}