use crate::{shell::ShellEnv, versions::Version, warning};
use anyhow::Result;
use directories::UserDirs;
use std::{
fs::{self, File},
io::{self, Read, Write},
path::{Path, PathBuf},
};
use whattheshell::Shell;
const CURRENT_VERSION_FILE: &str = ".current_version";
pub fn get_env_vars(shell: &Shell) -> Result<String> {
let path = std::env::var("PATH")?;
let vars = [
(
"PATH",
shell.append_to_path(&path, &shell.path_to_string(get_current_bin_dir()?)?)?,
),
("GOROOT", shell.path_to_string(get_current_install_dir()?)?),
];
let lines: Result<Vec<_>, _> = vars
.iter()
.map(|(k, v)| shell.get_setenv_command(k, v))
.collect();
Ok(lines?.join("\n"))
}
pub fn get_home_dir() -> Result<PathBuf> {
UserDirs::new()
.ok_or_else(|| anyhow::anyhow!("could not find user directories"))
.map(|dirs| dirs.home_dir().to_path_buf())
}
pub fn get_work_dir() -> Result<PathBuf> {
get_home_dir().map(|dir| dir.join(".local").join("goup"))
}
pub fn get_installations_dir() -> Result<PathBuf> {
get_work_dir().map(|dir| dir.join("installations"))
}
pub fn get_current_link_dir() -> Result<PathBuf> {
get_work_dir().map(|dir| dir.join("current"))
}
pub fn get_current_install_dir() -> Result<PathBuf> {
get_current_link_dir().map(|dir| dir.join("go"))
}
pub fn get_current_bin_dir() -> Result<PathBuf> {
get_current_install_dir().map(|dir| dir.join("bin"))
}
pub fn get_version_installation_dir(version: &Version) -> Result<PathBuf> {
get_installations_dir().map(|v| v.join(version.to_string()))
}
pub fn ensure_dir<P: AsRef<Path>>(path: P) -> Result<()> {
if !path.as_ref().exists() {
fs::create_dir_all(path)?;
}
Ok(())
}
pub fn get_current_version() -> Result<Option<Version>> {
let vfile_path = get_work_dir()?.join(CURRENT_VERSION_FILE);
let mut vfile = match File::open(vfile_path) {
Ok(f) => f,
Err(err) => {
if matches!(err.kind(), std::io::ErrorKind::NotFound) {
return Ok(None);
} else {
return Err(err.into());
}
}
};
let mut ver = String::new();
vfile.read_to_string(&mut ver)?;
let v = ver.parse()?;
Ok(Some(v))
}
pub fn get_installed_versions() -> Result<Vec<Version>> {
let dir = match get_installations_dir()?.read_dir() {
Ok(v) => v,
Err(err) if matches!(err.kind(), io::ErrorKind::NotFound) => return Ok(vec![]),
Err(err) => return Err(err.into()),
};
let dir: Result<Vec<_>, _> = dir.collect();
let versions: Result<Vec<Version>, _> = dir?
.iter()
.map(|v| v.file_name().to_string_lossy().parse())
.collect();
versions
}
pub fn write_current_version(version: Option<&Version>) -> Result<()> {
let vfile_path = get_work_dir()?.join(CURRENT_VERSION_FILE);
match version {
Some(v) => File::create(vfile_path)?.write_all(v.to_string().as_bytes())?,
None => fs::remove_file(vfile_path)?,
}
Ok(())
}
pub fn drop_version(version: &Version) -> Result<()> {
let dir = get_version_installation_dir(version)?;
fs::remove_dir_all(dir)?;
Ok(())
}
pub fn drop_install_dir() -> Result<()> {
let dir = get_installations_dir()?;
fs::remove_dir_all(dir)?;
Ok(())
}
pub fn read_profile(shell: &Shell) -> Result<String> {
let profile_dir = shell.get_profile_dir()?;
if !profile_dir.exists() {
return Ok(String::new());
}
let mut f = File::open(profile_dir)?;
let mut res = String::new();
f.read_to_string(&mut res)?;
Ok(res)
}
pub fn append_to_profile(shell: &Shell, content: &str) -> Result<()> {
let profile_dir = shell.get_profile_dir()?;
let mut f = if profile_dir.exists() {
File::options().append(true).open(profile_dir)?
} else {
if let Some(parent) = profile_dir.parent() {
if !parent.exists() {
fs::create_dir_all(parent)?;
}
}
File::create(profile_dir)?
};
f.write_all(content.as_bytes())?;
Ok(())
}
pub fn check_env_applied(shell: &Shell) -> Result<()> {
if !shell.is_env_applied()? {
warning!(
"Seems like necessary environment variables have not been applied. \
This results in the selected SDK version not being available in the terminal.\n\
Please see `goup help env` to setup required environment variables.\n"
);
}
Ok(())
}
#[cfg(windows)]
pub fn to_gitbash_path(pth: &str) -> String {
let pth = pth.replace('\\', "/");
let mut chars = pth.chars();
if chars.nth(1) == Some(':') && chars.next() == Some('/') {
format!("/{}{}", &pth[..1].to_lowercase(), &pth[2..])
} else {
pth
}
}
#[cfg(windows)]
pub fn to_gitbash_path_var(curr: &str) -> String {
curr.split(';')
.map(to_gitbash_path)
.collect::<Vec<_>>()
.join(":")
}
#[cfg(test)]
mod test {
#[cfg(windows)]
#[test]
fn test_to_gitbash_path() {
use super::*;
assert_eq!("/c/users/foo/bar", to_gitbash_path(r"C:\users\foo\bar"));
assert_eq!("/c/users/foo/bar", to_gitbash_path(r"C:/users/foo/bar"));
assert_eq!("users/foo/bar", to_gitbash_path(r"users\foo\bar"));
}
}