feat: auto-config file detection at startup (#281)
The TOML config file is now detected at startup time using a new
default `./config.toml` value. It makes sure to check this file path
and load it if found. Otherwise, the behavior continues as usual.
So it's not mandatory to provide a flag or env variable explicitly if
the default file path is wanted.
Also, additional debug information and file validations are provided.
Diff
src/bin/server.rs | 2 +-
src/server.rs | 10 ++++++++--
src/settings/cli.rs | 13 ++++++++++---
src/settings/mod.rs | 46 ++++++++++++++++++++++++++++------------------
src/winservice.rs | 12 +++++-------
5 files changed, 52 insertions(+), 31 deletions(-)
@@ -25,7 +25,7 @@ fn main() -> Result {
if let Some(commands) = opts.general.commands {
match commands {
Commands::Install {} => {
return winservice::install_service(opts.general.config_file);
return winservice::install_service(&opts.general.config_file);
}
Commands::Uninstall {} => {
return winservice::uninstall_service();
@@ -109,8 +109,14 @@ impl Server {
server_info!("log level: {}", general.log_level);
if let Some(config_file) = general.config_file {
server_info!("config file: {}", config_file.display());
let config_file = general.config_file;
if config_file.is_file() {
server_info!("config file used: {}", config_file.display());
} else {
tracing::debug!(
"config file path not found or not a regular file: {}",
config_file.display()
);
}
@@ -11,6 +11,7 @@ use std::path::PathBuf;
#[cfg(feature = "directory-listing")]
use crate::directory_listing::DirListFmt;
use crate::Result;
#[derive(Parser, Debug)]
@@ -344,9 +345,15 @@ pub struct General {
pub grace_period: u8,
#[arg(long, short = 'w', env = "SERVER_CONFIG_FILE")]
#[arg(
long,
short = 'w',
default_value = "./config.toml",
value_parser = value_parser_pathbuf,
env = "SERVER_CONFIG_FILE"
)]
pub config_file: Option<PathBuf>,
pub config_file: PathBuf,
#[arg(
long,
@@ -466,7 +473,7 @@ pub enum Commands {
Uninstall {},
}
fn value_parser_pathbuf(s: &str) -> crate::Result<PathBuf, String> {
fn value_parser_pathbuf(s: &str) -> Result<PathBuf, String> {
Ok(PathBuf::from(s))
}
@@ -11,7 +11,7 @@ use globset::{Glob, GlobMatcher};
use headers::HeaderMap;
use hyper::StatusCode;
use regex::Regex;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use crate::{helpers, logger, Context, Result};
@@ -161,11 +161,14 @@ impl Settings {
if let Some((settings, path_resolved)) = get_file_settings(opts.config_file)? {
config_file = Some(path_resolved);
if let Some((settings, config_file_resolved)) = read_file_settings(&opts.config_file)? {
config_file = config_file_resolved;
if let Some(general) = settings.general {
let has_general_settings = settings.general.is_some();
if has_general_settings {
let general = settings.general.unwrap();
if let Some(v) = general.host {
host = v
}
@@ -310,11 +313,20 @@ impl Settings {
}
}
if log_init {
logger::init(log_level.as_str())?;
}
tracing::debug!("toml configuration file read successfully");
tracing::debug!("config file read successfully");
tracing::debug!("config file path provided: {}", opts.config_file.display());
tracing::debug!("config file path resolved: {}", config_file.display());
if !has_general_settings {
server_warn!(
"config file empty or no `general` settings found, using default values"
);
}
if let Some(advanced) = settings.advanced {
@@ -473,7 +485,7 @@ impl Settings {
});
}
} else if log_init {
logger::init(log_level.as_str())?;
}
@@ -543,19 +555,17 @@ impl Settings {
}
}
fn get_file_settings(file_path_opt: Option<PathBuf>) -> Result<Option<(FileSettings, PathBuf)>> {
if let Some(ref file_path) = file_path_opt {
if file_path.is_file() {
let file_path_resolved = file_path
.canonicalize()
.with_context(|| "error resolving toml config file path")?;
fn read_file_settings(config_file: &Path) -> Result<Option<(FileSettings, PathBuf)>> {
if config_file.is_file() {
let file_path_resolved = config_file
.canonicalize()
.with_context(|| "unable to resolve toml config file path")?;
let settings = FileSettings::read(&file_path_resolved).with_context(|| {
"can not read toml config file because has invalid or unsupported format/options"
})?;
let settings = FileSettings::read(&file_path_resolved).with_context(|| {
"unable to read toml config file because has invalid format or unsupported options"
})?;
return Ok(Some((settings, file_path_resolved)));
}
return Ok(Some((settings, file_path_resolved)));
}
Ok(None)
}
@@ -9,7 +9,7 @@
use std::ffi::OsString;
use std::thread;
use std::time::Duration;
use std::{env, path::PathBuf};
use std::{env, path::Path};
use windows_service::{
define_windows_service,
@@ -174,7 +174,7 @@ pub fn run_server_as_service() -> Result {
}
pub fn install_service(config_file: Option<PathBuf>) -> Result {
pub fn install_service(config_file: &Path) -> Result {
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
@@ -185,11 +185,9 @@ pub fn install_service(config_file: Option<PathBuf>) -> Result {
let mut service_binary_arguments = vec![OsString::from("--windows-service=true")];
if let Some(f) = config_file {
let f = helpers::adjust_canonicalization(&f);
if !f.is_empty() {
service_binary_arguments.push(OsString::from(["--config-file=", &f].concat()));
}
let f = helpers::adjust_canonicalization(config_file);
if !f.is_empty() {
service_binary_arguments.push(OsString::from(["--config-file=", &f].concat()));
}