mod env;
mod error;
mod interface;
mod settings;
use std::path;
use console::style;
use lexa_wildcard_matching::WildcardMatching;
pub use self::error::Error;
use self::interface::ApplicationLoggerInterface;
pub use self::interface::{
Application,
ApplicationCLIInterface,
ApplicationEnvInterface,
ApplicationExt,
};
pub use self::settings::{Settings, SettingsLoaderExtension};
use crate::server::Server;
pub struct DefaultApplication<UserEnv, UserCLI, UserState> {
name: &'static str,
version: &'static str,
root_dir: path::PathBuf,
process_mode: env::EnvProcessMode,
env_vars: Option<UserEnv>,
cli_args: Option<UserCLI>,
settings: settings::Settings,
pub server: Server<UserState>,
}
impl<UE, UC, US> DefaultApplication<UE, UC, US>
where
UE: 'static,
UE: interface::ApplicationEnvInterface,
UC: 'static,
UC: interface::ApplicationCLIInterface,
US: 'static,
US: crate::state::StateInterface,
{
pub async fn create(
name: &'static str,
version: &'static str,
root_dir: impl AsRef<path::Path>,
loader_extension: impl Into<settings::SettingsLoaderExtension>,
) -> Result<Self, Error> {
let path_root_dir = root_dir.as_ref();
let settings = settings::Settings {
root_dir: path_root_dir.to_owned(),
config_dir: path_root_dir.join("config"),
loader_extension: loader_extension.into(),
};
let mut application = Self {
name,
version,
root_dir: path_root_dir.to_owned(),
server: Server::new(settings.clone())?,
settings,
process_mode: Default::default(),
cli_args: Default::default(),
env_vars: Default::default(),
};
let process_env_mode = if option_env!("DOCKER").is_some() {
env::EnvProcessMode::DEVELOPMENT
} else if cfg!(test) {
env::EnvProcessMode::TEST
} else if cfg!(debug_assertions) {
env::EnvProcessMode::LOCAL
} else {
env::EnvProcessMode::PRODUCTION
};
application = application
.define_process_mode(process_env_mode)
.using_logger()?
.with_cli_args()
.with_env_vars()?;
Ok(application)
}
pub fn routes(&self) -> impl Iterator<Item = &crate::routing::Route<US>> {
self.server.routes.all()
}
pub async fn run(mut self) -> Result<(), Error> {
log::debug!(
"Application: {}@{} ({})",
self.name,
style(self.version).blue(),
style(self.root_dir.display()).yellow(),
);
if let Some(cli_args) = self.cli_args {
self.server.router =
self.server.router.layer(axum::Extension(cli_args));
}
if let Some(env_vars) = self.env_vars {
self.server.router =
self.server.router.layer(axum::Extension(env_vars));
}
Ok(self.server.run().await?)
}
}
#[async_trait::async_trait]
impl<UE, UC, US> ApplicationExt for DefaultApplication<UE, UC, US>
where
UE: interface::ApplicationEnvInterface,
UC: interface::ApplicationCLIInterface,
{
type CLI = UC;
type ENV = UE;
fn define_process_mode(mut self, mode: env::EnvProcessMode) -> Self
where
Self: Sized,
{
self.process_mode = mode;
self
}
fn with_cli_args(mut self) -> Self {
let args = Self::CLI::arguments();
self.cli_args = Some(args);
self
}
fn with_env_vars(mut self) -> Result<Self, Error> {
let vars = Self::ENV::setup(&self.settings)?;
self.env_vars = Some(vars);
Ok(self)
}
}
impl<UE, UC, US> ApplicationLoggerInterface for DefaultApplication<UE, UC, US> {
fn using_logger(self) -> Result<Self, Error> {
let logger_settings: settings::LoggerSettings =
lexa_fs::load_or_default(
&self.settings.config_dir,
"logger",
self.settings.loader_extension,
);
let logger_settings = logger_settings.settings();
let mut logger = lexa_logger::stdout::Logger::builder();
logger = logger.with_level(logger_settings.max_level);
if logger_settings.colorized {
logger = logger.with_color();
}
if logger_settings.timestamp {
logger = logger.with_timestamp();
}
for filter in logger_settings.target_filters {
logger =
logger.filter(move |metadata| metadata.target().iswm(&filter));
}
logger.build_stdout()?;
Ok(self)
}
}