mod db_service;
mod i18n;
mod localized_template_list;
mod mail_service;
mod maintenance_mode;
mod prometheus;
mod rate_limiter;
mod routes;
pub mod state;
mod stateful_token_service;
mod stateless_token_service;
mod template;

use crate::app::config::Configuration;
use rocket::Build;
use rocket::Rocket;

// GOTCHA:
//     Initializer's `init` fn can fail and return error or initializer can be disabled e.g.,
//     by configuration in that case it has nothing to register in rocket i.e.,
//     `init` returns Ok(None)
type Result<T, E = anyhow::Error> = anyhow::Result<Option<T>, E>;
type InfallibleResult<T> = Result<T, std::convert::Infallible>;

macro_rules! initialize {
    ( rocket => $rocket:ident, config => $config:ident; $( $module:ident ),+ $(,)? ) => {{
        let rocket = $rocket;
        let config = &$config;
        $(
            let rocket = if let Some(value) = $module::init(config)? {
                $module::register(rocket, config, value)
            } else {
                rocket
            };
        )+

        rocket
    }};
}

pub fn run(rocket: Rocket<Build>, config: &Configuration) -> anyhow::Result<Rocket<Build>> {
    Ok(initialize!(rocket => rocket, config => config;
        // If you add a new initializer module under `hagrid::initializers`
        // (e.g. `src/initializers/your_new_initializer_module.rs`), add its initializer
        // name to this list so it is actually executed.
        template,
        maintenance_mode,
        i18n,
        state,
        stateless_token_service,
        stateful_token_service,
        mail_service,
        db_service,
        rate_limiter,
        localized_template_list,
        routes,
        prometheus,
    ))
}
