use super::{config, executor};
use anyhow::{Result, anyhow};
use clap_complete::Shell;
use colored::*;
use dialoguer::{Select, theme::ColorfulTheme};
use std::collections::HashMap;
use std::io::{self, Write};
use std::process::Stdio;
use zoi_core::utils;
pub fn setup(env_alias: Option<&str>, config: &config::ProjectConfig) -> Result<()> {
if config.environments.is_empty() {
return Err(anyhow!("No environments defined in zoi.yaml"));
}
let env_to_setup = match env_alias {
Some(alias) => config
.environments
.iter()
.find(|e| e.cmd == alias)
.ok_or_else(|| anyhow!("Environment '{alias}' not found in zoi.yaml"))?
.clone(),
None => {
let selections: Vec<&str> = config
.environments
.iter()
.map(|e| e.name.as_str())
.collect();
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Choose an environment to set up")
.items(&selections)
.default(0)
.interact_opt()?
.ok_or(anyhow!("No environment chosen."))?;
config.environments[selection].clone()
}
};
println!(
"\n{} Setting up environment: {}...",
"::".bold().blue(),
env_to_setup.name.bold()
);
check_packages(config)?;
let platform = utils::get_platform()?;
let run_cmds = match &env_to_setup.run {
config::PlatformOrStringVec::StringVec(v) => v.clone(),
config::PlatformOrStringVec::Platform(p) => p
.get(&platform)
.or_else(|| p.get("default"))
.cloned()
.ok_or_else(|| {
anyhow!(
"No commands found for platform '{}' and no default specified",
platform
)
})?,
};
let env_vars = match &env_to_setup.env {
config::PlatformOrEnvMap::EnvMap(m) => m.clone(),
config::PlatformOrEnvMap::Platform(p) => p
.get(&platform)
.or_else(|| p.get("default"))
.cloned()
.unwrap_or_default(),
};
for cmd_str in &run_cmds {
executor::run_shell_command(cmd_str, &env_vars)?;
}
Ok(())
}
fn check_packages(config: &config::ProjectConfig) -> Result<()> {
if config.packages.is_empty() {
return Ok(());
}
println!("\nChecking required packages...");
let mut all_ok = true;
for package in &config.packages {
print!("- Checking for '{}': ", package.name.cyan());
let _ = io::stdout().flush();
let status = executor::get_shell_command(&package.check)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
if status.success() {
println!("{}", "OK".green());
} else {
println!("{}", "MISSING".red());
all_ok = false;
}
}
if !all_ok {
return Err(anyhow!("One or more required packages are missing."));
}
Ok(())
}
pub fn export_shell(
env_alias: Option<&str>,
config: &config::ProjectConfig,
shell: Shell,
) -> Result<()> {
let platform = utils::get_platform()?;
let mut env_vars = HashMap::new();
if let Some(alias) = env_alias {
let env_spec = config
.environments
.iter()
.find(|e| e.cmd == alias)
.ok_or_else(|| anyhow!("Environment '{alias}' not found"))?;
let extra_env = match &env_spec.env {
config::PlatformOrEnvMap::EnvMap(m) => m.clone(),
config::PlatformOrEnvMap::Platform(p) => p
.get(&platform)
.or_else(|| p.get("default"))
.cloned()
.unwrap_or_default(),
};
env_vars.extend(extra_env);
}
if config.config.local {
let bin_dir = std::env::current_dir()?
.join(".zoi")
.join("pkgs")
.join("bin");
if bin_dir.exists() {
let mut path = bin_dir.to_string_lossy().to_string();
if let Ok(old_path) = std::env::var("PATH") {
path = format!(
"{}{}{}",
path,
if cfg!(windows) { ";" } else { ":" },
old_path
);
}
env_vars.insert("PATH".to_string(), path);
}
}
for (k, v) in env_vars {
match shell {
Shell::Bash | Shell::Zsh => {
println!("export {}=\"{}\"", k, v);
}
Shell::Fish => {
println!("set -gx {} \"{}\"", k, v);
}
Shell::PowerShell => {
println!("$env:{} = \"{}\"", k, v);
}
Shell::Elvish => {
println!("set E:{} = \"{}\"", k, v);
}
_ => {}
}
}
Ok(())
}