zoi_project/
environment.rs1use super::{config, executor};
2use anyhow::{Result, anyhow};
3use clap_complete::Shell;
4use colored::*;
5use dialoguer::{Select, theme::ColorfulTheme};
6use std::collections::HashMap;
7use std::io::{self, Write};
8use std::process::Stdio;
9use zoi_core::utils;
10
11pub fn setup(env_alias: Option<&str>, config: &config::ProjectConfig) -> Result<()> {
12 if config.environments.is_empty() {
13 return Err(anyhow!("No environments defined in zoi.yaml"));
14 }
15
16 let env_to_setup = match env_alias {
17 Some(alias) => config
18 .environments
19 .iter()
20 .find(|e| e.cmd == alias)
21 .ok_or_else(|| anyhow!("Environment '{alias}' not found in zoi.yaml"))?
22 .clone(),
23 None => {
24 let selections: Vec<&str> = config
25 .environments
26 .iter()
27 .map(|e| e.name.as_str())
28 .collect();
29 let selection = Select::with_theme(&ColorfulTheme::default())
30 .with_prompt("Choose an environment to set up")
31 .items(&selections)
32 .default(0)
33 .interact_opt()?
34 .ok_or(anyhow!("No environment chosen."))?;
35
36 config.environments[selection].clone()
37 }
38 };
39
40 println!(
41 "\n{} Setting up environment: {}...",
42 "::".bold().blue(),
43 env_to_setup.name.bold()
44 );
45
46 check_packages(config)?;
47
48 let platform = utils::get_platform()?;
49
50 let run_cmds = match &env_to_setup.run {
51 config::PlatformOrStringVec::StringVec(v) => v.clone(),
52 config::PlatformOrStringVec::Platform(p) => p
53 .get(&platform)
54 .or_else(|| p.get("default"))
55 .cloned()
56 .ok_or_else(|| {
57 anyhow!(
58 "No commands found for platform '{}' and no default specified",
59 platform
60 )
61 })?,
62 };
63
64 let env_vars = match &env_to_setup.env {
65 config::PlatformOrEnvMap::EnvMap(m) => m.clone(),
66 config::PlatformOrEnvMap::Platform(p) => p
67 .get(&platform)
68 .or_else(|| p.get("default"))
69 .cloned()
70 .unwrap_or_default(),
71 };
72
73 for cmd_str in &run_cmds {
74 executor::run_shell_command(cmd_str, &env_vars)?;
75 }
76
77 Ok(())
78}
79
80fn check_packages(config: &config::ProjectConfig) -> Result<()> {
81 if config.packages.is_empty() {
82 return Ok(());
83 }
84 println!("\nChecking required packages...");
85 let mut all_ok = true;
86 for package in &config.packages {
87 print!("- Checking for '{}': ", package.name.cyan());
88 let _ = io::stdout().flush();
89
90 let status = executor::get_shell_command(&package.check)
91 .stdout(Stdio::null())
92 .stderr(Stdio::null())
93 .status()?;
94
95 if status.success() {
96 println!("{}", "OK".green());
97 } else {
98 println!("{}", "MISSING".red());
99 all_ok = false;
100 }
101 }
102 if !all_ok {
103 return Err(anyhow!("One or more required packages are missing."));
104 }
105 Ok(())
106}
107
108pub fn export_shell(
109 env_alias: Option<&str>,
110 config: &config::ProjectConfig,
111 shell: Shell,
112) -> Result<()> {
113 let platform = utils::get_platform()?;
114 let mut env_vars = HashMap::new();
115
116 if let Some(alias) = env_alias {
117 let env_spec = config
118 .environments
119 .iter()
120 .find(|e| e.cmd == alias)
121 .ok_or_else(|| anyhow!("Environment '{alias}' not found"))?;
122
123 let extra_env = match &env_spec.env {
124 config::PlatformOrEnvMap::EnvMap(m) => m.clone(),
125 config::PlatformOrEnvMap::Platform(p) => p
126 .get(&platform)
127 .or_else(|| p.get("default"))
128 .cloned()
129 .unwrap_or_default(),
130 };
131 env_vars.extend(extra_env);
132 }
133
134 if config.config.local {
135 let bin_dir = std::env::current_dir()?
136 .join(".zoi")
137 .join("pkgs")
138 .join("bin");
139 if bin_dir.exists() {
140 let mut path = bin_dir.to_string_lossy().to_string();
141 if let Ok(old_path) = std::env::var("PATH") {
142 path = format!(
143 "{}{}{}",
144 path,
145 if cfg!(windows) { ";" } else { ":" },
146 old_path
147 );
148 }
149 env_vars.insert("PATH".to_string(), path);
150 }
151 }
152
153 for (k, v) in env_vars {
154 match shell {
155 Shell::Bash | Shell::Zsh => {
156 println!("export {}=\"{}\"", k, v);
157 }
158 Shell::Fish => {
159 println!("set -gx {} \"{}\"", k, v);
160 }
161 Shell::PowerShell => {
162 println!("$env:{} = \"{}\"", k, v);
163 }
164 Shell::Elvish => {
165 println!("set E:{} = \"{}\"", k, v);
166 }
167 _ => {}
168 }
169 }
170
171 Ok(())
172}