use std::{env, fs, io, path::Path};
pub mod constant;
pub mod init_interactive;
use constant::*;
use anyhow::{Context, Result as AppResult, bail};
#[derive(Clone)]
pub struct EnvArgs {
pub config_file_path: Option<String>,
pub port: Option<u16>,
pub fallback_respond_dir_path: Option<String>,
}
impl EnvArgs {
pub fn default() -> AppResult<Option<Self>> {
let mut ret = EnvArgs::from_args()?;
let init_config = args_option_value(INIT_CONFIG_OPTION_NAMES.as_ref()).is_some();
if init_config {
let includes_middleware =
args_option_value(INCLUDES_MIDDLEWARE_OPTION_NAMES.as_ref()).is_some();
let force_defaults = args_option_value(YES_OPTION_NAMES.as_ref()).is_some();
if let Err(err) = ret.init_config_interactive(includes_middleware, force_defaults) {
log::error!("failed to init config ({})", err);
}
return Ok(None);
}
ret.default_config_file_path();
ret.validate()?;
Ok(Some(ret))
}
pub fn validate(&self) -> AppResult<()> {
if let Some(config_file_path) = self.config_file_path.as_ref() {
if !Path::new(config_file_path.as_str()).exists() {
bail!(
"config file specified via --config does not exist: {}",
config_file_path
);
}
}
if let Some(fallback_respond_dir_path) = self.fallback_respond_dir_path.as_ref() {
if !Path::new(fallback_respond_dir_path.as_str()).exists() {
bail!(
"fallback response dir specified via --dir does not exist: {}",
fallback_respond_dir_path
);
}
}
Ok(())
}
fn from_args() -> AppResult<Self> {
let port = match args_option_value(CONFIG_LISTENER_PORT_OPTION_NAMES.as_ref()) {
Some(port_str) => Some(port_str.parse::<u16>().with_context(|| {
format!("--port value is not a valid u16: {}", port_str)
})?),
None => None,
};
Ok(EnvArgs {
config_file_path: args_option_value(CONFIG_FILE_PATH_OPTION_NAMES.as_ref()),
port,
fallback_respond_dir_path: args_option_value(
FALLBACK_RESPOND_DIR_PATH_OPTION_NAMES.as_ref(),
),
})
}
fn init_config_interactive(
&mut self,
cli_middleware_override: bool,
force_defaults: bool,
) -> Result<(), io::Error> {
if Path::new(DEFAULT_CONFIG_FILE_PATH).exists() {
println!(
"[warn] quit because default root config file exists: {}.",
DEFAULT_CONFIG_FILE_PATH
);
return Ok(());
}
let answers = init_interactive::run(force_defaults, cli_middleware_override)?;
if answers.include_middleware {
if !Path::new(DEFAULT_MIDDLEWARE_FILE_PATH).exists() {
let content =
include_str!("../examples/config/default/apimock-middleware.rhai");
fs::write(DEFAULT_MIDDLEWARE_FILE_PATH, content)?;
println!(
"middleware scripting file is created: {}.",
DEFAULT_MIDDLEWARE_FILE_PATH
);
} else {
println!(
"[warn] middleware scripting file exists: {}.",
DEFAULT_MIDDLEWARE_FILE_PATH
);
}
}
let config_content = init_interactive::render_apimock_toml(&answers);
fs::write(DEFAULT_CONFIG_FILE_PATH, config_content)?;
println!("root config file is created: {}.", DEFAULT_CONFIG_FILE_PATH);
if answers.include_rule_set && !Path::new(DEFAULT_RULE_SET_FILE_PATH).exists() {
let rule_set_content =
include_str!("../examples/config/default/apimock-rule-set.toml");
fs::write(DEFAULT_RULE_SET_FILE_PATH, rule_set_content)?;
println!(
"rule set config file is created: {}.",
DEFAULT_RULE_SET_FILE_PATH
);
}
init_interactive::print_summary(&answers);
Ok(())
}
fn default_config_file_path(&mut self) {
if self.config_file_path.is_some() {
return;
}
if !Path::new(DEFAULT_CONFIG_FILE_PATH).exists() {
return;
}
self.config_file_path = Some(DEFAULT_CONFIG_FILE_PATH.to_owned());
}
}
fn args_option_value(option_names: &[&str]) -> Option<String> {
let args: Vec<String> = env::args().collect();
let name_index = args
.iter()
.position(|arg| option_names.iter().any(|n| arg.as_str() == *n))?;
let name_value = args.get(name_index + 1);
match name_value {
Some(v) if !v.starts_with('-') => Some(v.to_owned()),
_ => Some(String::new()),
}
}