use std::{
env::var,
io::Write,
path::{Path, PathBuf},
};
use crate::{Config, CONFIG_FNAME, DEFAULT_CONFIG_DIR, ENV_CONFIG_DIR};
pub fn install() {
print_header("Install Wireman");
let mut config_dir = var(ENV_CONFIG_DIR).unwrap_or_else(|_| DEFAULT_CONFIG_DIR.to_string());
let input = read_input(&format!(
"Do you want to install Wireman to \x1b[1;33m{config_dir}\x1b[0m? [y/N] "
));
if input.trim().to_lowercase() != "y" {
let input = read_input("Install instead in: ");
config_dir = input.trim().to_string();
}
let is_default_directory = expand_path(&config_dir) == expand_path(DEFAULT_CONFIG_DIR);
if let Err(err) = create_directory_if_missing(expand_path(&config_dir)) {
println!("\x1b[1;31mError:\x1b[0m Could not create {config_dir:?}: {err}. ABORT.");
return;
}
match write_config_to_toml(expand_path(&config_dir)) {
Ok(should_continue) if !should_continue => return,
Err(err) => {
println!("\x1b[1;31mError:\x1b[0m Could not write config: {err}. ABORT.");
return;
}
_ => {}
}
println!();
print_header("Further Information");
println!();
println!("\x1b[1;34mWireman configuration file:\x1b[0m");
println!();
println!(" \x1b[0;37m{config_dir}/{CONFIG_FNAME}\x1b[0m");
println!();
if !is_default_directory {
println!("\x1b[1;33mAdd this line to your shell configuration file:\x1b[0m");
println!();
println!(" \x1b[0;37mexport {ENV_CONFIG_DIR}={config_dir}\x1b[0m");
println!();
}
println!("\x1b[1;34mInclude directories and proto files:\x1b[0m");
println!();
println!(" \x1b[0;37mincludes = [");
println!(" \"$HOME/my-project/services\",");
println!(" \"$HOME/my-project/protos\",");
println!(" ]");
println!(" files = [");
println!(" \"order/api.proto\",");
println!(" \"price/api.proto\"");
println!(" ]\x1b[0m");
println!();
println!("\x1b[1;36mMore information:\x1b[0m");
println!(" \x1b[0;34mhttps://github.com/preiter93/wireman?tab=readme-ov-file#setupconfiguration\x1b[0m");
println!();
println!("\x1b[1;32mSetup complete! You're ready to use Wireman.\x1b[0m");
}
fn print_header(text: &str) {
println!("\x1b[1;33m{}\x1b[0m", text);
}
fn read_input(prompt: &str) -> String {
print!("{prompt}: ");
std::io::stdout().flush().unwrap();
let mut input_string = String::new();
std::io::stdin().read_line(&mut input_string).unwrap();
input_string
}
pub(crate) fn expand_path(path: &str) -> String {
enforce_absolute_path(
shellexpand::tilde(
&shellexpand::env(&expand_current_dir(path))
.map_or(path.to_string(), |x| x.to_string()),
)
.as_ref(),
)
}
pub(crate) fn expand_file(path: &str) -> String {
shellexpand::tilde(
&shellexpand::env(&expand_current_dir(path)).map_or(path.to_string(), |x| x.to_string()),
)
.to_string()
}
pub(crate) fn make_absolute_path(config_path: &str) -> String {
let path = Path::new(config_path);
if path.is_absolute()
|| config_path.starts_with('.')
|| config_path.starts_with('~')
|| config_path.starts_with('$')
{
config_path.to_string()
} else {
std::env::current_dir()
.unwrap_or_else(|_| PathBuf::from("."))
.join(path)
.to_string_lossy()
.to_string()
}
}
fn create_directory_if_missing<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
let path = path.as_ref();
if !path.exists() {
std::fs::create_dir_all(path)?;
}
Ok(())
}
fn write_config_to_toml<P: AsRef<Path>>(path: P) -> std::io::Result<bool> {
let file_path = path.as_ref().join(CONFIG_FNAME);
if file_path.exists() {
println!("It seems you already have a config file.");
return Ok(true);
}
let mut config = Config::default();
config.server.default_address = Some(String::from("http://localhost:50051"));
config.server.default_auth_header = Some(String::new());
let toml_str = toml::to_string(&config).expect("Failed to serialize config");
std::fs::write(&file_path, toml_str)?;
std::fs::create_dir_all(path)?;
Ok(true)
}
fn enforce_absolute_path(path: &str) -> String {
if path.starts_with('/') {
path.to_string()
} else {
format!("/{path}")
}
}
fn expand_current_dir(path: &str) -> String {
if let Some(stripped) = path.strip_prefix('.') {
std::env::current_dir()
.unwrap_or_else(|_| PathBuf::from("."))
.join(stripped)
.to_string_lossy()
.to_string()
} else {
path.to_string()
}
}