diff --git a/Cargo.toml b/Cargo.toml index 3d3e189..2edf008 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "sandbox" -version = "0.1.1" +version = "0.2.1" edition = "2021" authors = ["WillKirkmanM "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.2.4", features = ["derive"] } +clap = { version = "4.2.7", features = ["derive"] } colored = "2.0.0" flate2 = "1.0.26" indicatif = "0.17.3" diff --git a/src/args.rs b/src/args.rs index b6b2df9..d3a519e 100644 --- a/src/args.rs +++ b/src/args.rs @@ -7,10 +7,6 @@ pub struct SandboxArgs { #[clap(short, long, default_value = "")] pub new: String, - /// Create a New Environment - #[clap(short = 'U', long, default_value = "")] - pub uninstall: String, - /// Search for Environment #[clap(short = 'S', long, default_value = "")] pub search: String, @@ -18,5 +14,18 @@ pub struct SandboxArgs { /// Search for Environment #[clap(short = 'I', long, default_value = "")] pub install: String, + + /// Create a New Environment + #[clap(short = 'U', long, default_value = "")] + pub uninstall: String, + + /// Reinstall an Environment + #[clap(short = 'R', long)] + pub reinstall: String, + + /// Clear the Install Cache + #[clap(short = 'C', long)] + pub clearcache: bool, + } diff --git a/src/cache.rs b/src/cache.rs new file mode 100644 index 0000000..946d66f --- /dev/null +++ b/src/cache.rs @@ -0,0 +1,41 @@ +use std::path::Path; + +use tokio::fs; +use colored::Colorize; + +use crate::{get_path, get_cache_path}; + +pub async fn ensure_installed() { + let cache_path = get_cache_path(); + + fs::create_dir_all(cache_path).await.unwrap(); +} + +pub async fn clear_cache() { + let cache_path = get_cache_path(); + + let cache_folder_is_empty = fs::read_dir(&cache_path).await.unwrap().next_entry().await.unwrap().is_none(); + + if cache_folder_is_empty { + println!("The Install Cache is {}!", "Empty".bright_green()) + } else { + fs::remove_dir_all(&cache_path).await.unwrap(); + fs::create_dir(&cache_path).await.unwrap(); + + println!("{} Cleared The Install Cache!", "Successfully".bright_green()) + } +} + +pub async fn in_cache(id: impl Into) -> bool { + let id = id.into(); + + let cache_path = get_cache_path(); + + let environment_path = get_path(&id).await; + let tar_name = environment_path.split('/').last().unwrap(); + + let formatted_cache_path = format!("{}{}", cache_path, tar_name); + let tar_in_cache = Path::new(&formatted_cache_path).exists(); + + tar_in_cache +} diff --git a/src/download.rs b/src/download.rs index 0bb081f..f5b5a08 100644 --- a/src/download.rs +++ b/src/download.rs @@ -5,7 +5,6 @@ use std::path::Path; use std::thread; use std::time::Duration; use std::fs; -use std::env; use std::{cmp::min, fmt::Write}; @@ -15,66 +14,104 @@ use tar::Archive; use indicatif::{ProgressBar, ProgressState, ProgressStyle}; use colored::Colorize; +use crate::get_beaches_path; +use crate::get_cache_path; use crate::get_path; +use crate::cache::{self, in_cache}; -pub async fn install(id: String) -> Result<(), Box> { - let base_path = match env::consts::OS { - "windows" => { - let appdata = std::env::var("appdata").unwrap(); - let beaches_path = format!("{}/sandbox/beaches/", appdata); - beaches_path - } - _ => "/usr/share/sandbox/beaches/".to_string() - }; +pub async fn install(id: impl Into) -> Result<(), Box> { + let id = id.into(); - let environment_path = get_path(id.clone()).await; + let base_path = get_beaches_path(); + let environment_path = get_path(&id).await; let download_url = format!("https://github.com/the-sandbox-project/sandbox-templates/raw/master/{}", environment_path); let download_path = format!("{}{}", base_path, environment_path); - let client = Client::new(); - - let response = client.get(download_url).send().await?; - - if response.status().is_success() { - let language_path = Path::new(&download_path).parent().unwrap().to_str().unwrap(); - fs::create_dir_all(language_path)?; + let tar_name = environment_path.split('/').last().unwrap(); + + cache::ensure_installed().await; + let cache_path = get_cache_path(); - let mut file = File::create(&download_path)?; + let formatted_cache_path = format!("{}{}", cache_path, tar_name); - let mut downloaded = 0; - let total_size = response.content_length().unwrap(); + if in_cache(&id).await { + println!("{} found in {}! Installing from {}...", id.blue(), "Cache".bright_green(), "Cache".bright_green()); - let pb = ProgressBar::new(total_size); - pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})") - .unwrap() - .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()) - .progress_chars("#>-")); + let language_path = Path::new(&download_path).parent().unwrap().to_str().unwrap(); - while downloaded < total_size { - let new = min(downloaded + 223211, total_size); - downloaded = new; - pb.set_position(new); - thread::sleep(Duration::from_millis(12)); - } - - pb.finish_with_message("downloaded"); + let unzip_path = format!("{}/{}", language_path, id); - let content = response.bytes().await?; + fs::create_dir_all(&language_path)?; - io::copy(&mut content.as_ref(), &mut file)?; - - let unzip_path = format!("{}/{}", language_path, id); + fs::copy(&formatted_cache_path, &download_path)?; let tar_gz = File::open(&download_path)?; let tar = GzDecoder::new(tar_gz); let mut archive = Archive::new(tar); archive.unpack(&unzip_path)?; + + File::create(&formatted_cache_path)?; + + fs::copy(&download_path, &formatted_cache_path)?; + fs::remove_file(&download_path)?; - println!("Installed {}! Test it out with: sandbox --new {}", id.bright_green(), id.bright_green()) - } - Ok(()) + println!("Installed {}! Test it out with: sandbox --new {}", id.bright_green(), id.bright_green()); + + Ok(()) + } else { + let client = Client::new(); + + let response = client.get(download_url).send().await?; + + if response.status().is_success() { + let language_path = Path::new(&download_path).parent().unwrap().to_str().unwrap(); + + fs::create_dir_all(language_path)?; + + let mut file = File::create(&download_path)?; + + let mut downloaded = 0; + let total_size = response.content_length().unwrap(); + + let pb = ProgressBar::new(total_size); + pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})") + .unwrap() + .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()) + .progress_chars("#>-")); + + while downloaded < total_size { + let new = min(downloaded + 223211, total_size); + downloaded = new; + pb.set_position(new); + thread::sleep(Duration::from_millis(12)); + } + + pb.finish_with_message("downloaded"); + + let content = response.bytes().await?; + + io::copy(&mut content.as_ref(), &mut file)?; + + let unzip_path = format!("{}/{}", language_path, id); + + let tar_gz = File::open(&download_path)?; + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + + archive.unpack(&unzip_path)?; + + File::create(&formatted_cache_path)?; + + fs::copy(&download_path, &formatted_cache_path)?; + + fs::remove_file(&download_path)?; + + println!("Installed {}! Test it out with: sandbox --new {}", id.bright_green(), id.bright_green()) + } + Ok(()) + } } diff --git a/src/environment.rs b/src/environment.rs index df3eb25..f291db8 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -7,7 +7,7 @@ use crate::get_path; pub async fn open_environment(environment: String) { let editor = get_editor(); - let path = get_path(environment.clone()).await; + let path = get_path(&environment).await; let environment_path = path.split("/").collect::>()[0].to_owned() + "/" + &environment; let beaches_path = match env::consts::OS { diff --git a/src/lib.rs b/src/lib.rs index 9b060fb..4ad21b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,9 @@ mod environment; mod search; mod install; mod uninstall; +mod reinstall; mod download; +mod cache; mod new; use std::error::Error; @@ -16,7 +18,9 @@ use args::SandboxArgs; use search::search; use install::install_environment; use uninstall::uninstall_environment; +use reinstall::reinstall_environment; use new::create_new_environment; +use cache::clear_cache; use clap::Parser; use serde_yaml::{Value, Mapping}; @@ -40,6 +44,14 @@ pub async fn run() { if !args.uninstall.is_empty() { uninstall_environment(args.uninstall).await; } + + if !args.reinstall.is_empty() { + reinstall_environment(args.reinstall).await; + } + + if args.clearcache { + clear_cache().await; + } } pub async fn get_templates_mapping() -> Result> { @@ -140,16 +152,9 @@ pub async fn id_is_valid(id: impl Into) -> bool{ pub async fn in_system(id: impl Into) -> bool { let id = id.into(); - let base_path = match env::consts::OS { - "windows" => { - let appdata = std::env::var("appdata").unwrap(); - let beaches_path = format!("{}/sandbox/beaches/", appdata); - beaches_path - } - _ => "/usr/share/sandbox/beaches/".to_string(), - }; + let base_path = get_beaches_path(); - let path = get_path(id.clone()).await; + let path = get_path(&id).await; let environment_path = path.split("/").collect::>()[0].to_owned() + "/" + &id; let formatted_path = format!("{}{}", base_path, environment_path); @@ -161,3 +166,29 @@ pub async fn in_system(id: impl Into) -> bool { false } } + +pub fn get_beaches_path() -> String { + match env::consts::OS { + "windows" => { + let appdata = env::var("APPDATA").unwrap(); + let beaches_path = format!("{}/sandbox/beaches/", appdata); + beaches_path + } + _ => "/usr/share/sandbox/beaches/".to_string(), + } +} + +pub fn get_cache_path() -> String { + match env::consts::OS { + "windows" => { + let appdata = env::var("LOCALAPPDATA").unwrap(); + let beaches_path = format!("{}/Temp/sandbox/", appdata); + beaches_path + } + _ => { + let home_path = env::var("HOME").unwrap(); + let cache_path = format!("{}/.cache/sandbox/", home_path); + cache_path + } + } +} diff --git a/src/reinstall.rs b/src/reinstall.rs new file mode 100644 index 0000000..94a9e61 --- /dev/null +++ b/src/reinstall.rs @@ -0,0 +1,22 @@ +use crate::id_is_valid; +use crate::download::install; +use crate::uninstall::uninstall; + +use colored::Colorize; + +pub async fn reinstall_environment(id: impl Into) { + let id = id.into(); + + if id_is_valid(&id).await { + reinstall(&id).await; + } else { + println!("The environment ({}) does {} exist! You can search for an environment with\nsandbox --search {}", id.bright_green(), "not".red(), id.bright_green()); + } +} + +pub async fn reinstall(id: impl Into) { + let id = id.into(); + + uninstall(&id).await; + install(&id).await.unwrap(); +} diff --git a/src/uninstall.rs b/src/uninstall.rs index a0ca21f..60e3163 100644 --- a/src/uninstall.rs +++ b/src/uninstall.rs @@ -1,7 +1,6 @@ -use std::env; use std::io::{stdin, stdout, Write}; -use crate::{get_path, id_is_valid, in_system}; +use crate::{get_path, get_beaches_path, id_is_valid, in_system}; use colored::Colorize; use tokio::fs; @@ -28,16 +27,9 @@ pub async fn uninstall_environment(environment: impl Into) { pub async fn uninstall(environment: impl Into) { let environment = environment.into(); - let base_path = match env::consts::OS { - "windows" => { - let appdata = std::env::var("appdata").unwrap(); - let beaches_path = format!("{}/sandbox/beaches/", appdata); - beaches_path - } - _ => "/usr/share/sandbox/beaches/".to_string(), - }; + let base_path = get_beaches_path(); - let path = get_path(environment.clone()).await; + let path = get_path(&environment).await; let environment_path = path.split("/").collect::>()[0].to_owned() + "/" + &environment; let formatted_path = format!("{}{}", base_path, environment_path);