diff --git a/Cargo.lock b/Cargo.lock index 9098dcb..56c56ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,7 +1221,7 @@ dependencies = [ [[package]] name = "runrs" -version = "1.1.8" +version = "1.1.9" dependencies = [ "argh", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index f886ff2..aa185d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runrs" -version = "1.1.8" +version = "1.1.9" edition = "2018" license = "MIT/Apache-2.0" description = "A Tool for Run Rust/TypeScript Scripts" diff --git a/src/main.rs b/src/main.rs index 0f45810..7a72a68 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,8 +31,11 @@ struct RunScriptArgs { #[argh(switch, short = 'i')] install: bool, /// update listed scripts - #[argh(switch)] + #[argh(switch, short = 'U')] update_listed_scripts: bool, + /// force update https script file + #[argh(switch)] + force_update: bool, /// script repo #[argh(option, short = 'R')] script_repo: Option, diff --git a/src/resolver.rs b/src/resolver.rs index 57fb1ea..a6e02a2 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,10 +1,31 @@ use crate::util; +use rust_util::util_time::get_current_millis; use rust_util::XResult; +use serde::{Deserialize, Serialize}; use std::fs; use std::path::Path; +#[allow(unreachable_code)] +fn get_script_cache_dir() -> &'static str { + #[cfg(feature = "switch-rust-lang")] + { + return "runrs"; + } + #[cfg(feature = "switch-ts-lang")] + { + return "runts"; + } + failure_and_exit!("Only rust/ts script supported."); +} + +#[derive(Debug, Serialize, Deserialize)] +struct HttpsScriptMeta { + url: String, + download_time: u128, +} + // download from internet if starts with https:// -pub fn resolve_file(script_file: &str) -> XResult { +pub fn resolve_file(script_file: &str, force_update: bool) -> XResult { if script_file.starts_with("http://") { return simple_error!("Insecure script file: {}", script_file); } @@ -14,23 +35,55 @@ pub fn resolve_file(script_file: &str) -> XResult { let file_sha256 = sha256::digest(script_file); let file_name = get_file_name(script_file)?; let cache_file_path = format!( - "{}/.cache/runrs/{}/{}", + "{}/.cache/{}/{}/{}", util::get_user_home_or_die(), + get_script_cache_dir(), file_sha256, file_name ); - let cache_file_path_url = format!( - "{}/.cache/runrs/{}.url", + let cache_file_path_meta = format!( + "{}/.cache/{}/{}.meta", util::get_user_home_or_die(), + get_script_cache_dir(), file_sha256 ); - debugging!("Cache file: {}", cache_file_path); - if let Ok(metadata) = fs::metadata(&cache_file_path) { - if metadata.is_file() { + let is_script_path_latest_version = script_file.contains("/@latest/"); + let cache_file_path_exists = fs::metadata(&cache_file_path).is_ok(); + debugging!( + "Cache file: {}, is latest version: {}, cache file exists: {}", + cache_file_path, + is_script_path_latest_version, + cache_file_path_exists + ); + + if force_update { + debugging!("Force update is on, skip cache file check"); + } else if is_script_path_latest_version { + // force update is not on, and script file is @latest version + if let Ok(cache_file_path_meta_content) = fs::read_to_string(&cache_file_path_meta) { + if let Ok(http_script_meta) = + serde_json::from_str::(&cache_file_path_meta_content) + { + let current_millis = get_current_millis(); + debugging!( + "Read from meta: {:?}, current millis: {}", + http_script_meta, + current_millis + ); + if current_millis - http_script_meta.download_time < 12 * 60 * 60 * 1000 + && cache_file_path_exists + { + // script file is download in 12 hours + return Ok(cache_file_path); + } + } + } + } else { + // force update is not on, and script is static version, check file existence only + if cache_file_path_exists { debugging!("Found cache file: {}", cache_file_path); return Ok(cache_file_path); } - return simple_error!("Cache file is not a file: {}", cache_file_path); } // create cache file parent path if not exists @@ -59,13 +112,31 @@ pub fn resolve_file(script_file: &str) -> XResult { let get_script_response_result = reqwest::blocking::get(script_file); debugging!("Get script response: {:#?}", &get_script_response_result); let get_script_response = match get_script_response_result { - Err(e) => return simple_error!("Get script failed: {}", e), + Err(e) => { + if cache_file_path_exists { + warning!( + "Get script failed, reuse local cache file: {}", + cache_file_path + ); + return Ok(cache_file_path); + } + return simple_error!("Get script failed: {}", e); + } Ok(response) => response, }; let get_script_response_status = get_script_response.status().as_u16(); if get_script_response_status == 404 { + // when 404 do not reuse local cache file return simple_error!("Script not found!"); } else if get_script_response_status != 200 { + if cache_file_path_exists { + warning!( + "Get script failed, status: {}, reuse local cache file: {}", + get_script_response_status, + cache_file_path + ); + return Ok(cache_file_path); + } return simple_error!("Get script failed: {}", get_script_response_status); } let remote_script_content = match get_script_response.text() { @@ -78,8 +149,14 @@ pub fn resolve_file(script_file: &str) -> XResult { if let Err(e) = fs::write(&cache_file_path, remote_script_content) { return simple_error!("Write script: {} failed: {}", cache_file_path, e); } - if let Err(_) = fs::write(&cache_file_path_url, script_file) { - // JUST IGNORE + let meta = HttpsScriptMeta { + url: script_file.to_string(), + download_time: get_current_millis(), + }; + if let Ok(meta_json) = serde_json::to_string_pretty(&meta) { + if let Err(_) = fs::write(&cache_file_path_meta, &meta_json) { + // JUST IGNORE + } } Ok(cache_file_path) diff --git a/src/run_rs.rs b/src/run_rs.rs index d40c550..8a75485 100644 --- a/src/run_rs.rs +++ b/src/run_rs.rs @@ -8,7 +8,7 @@ pub fn do_run_script(args: &RunScriptArgs) { failure_and_exit!("Must assign a script file name"); } let script_file = &args.arguments[0]; - let script_file = resolve_file(script_file) + let script_file = resolve_file(script_file, args.force_update) .unwrap_or_else(|e| failure_and_exit!("Failed to resolve script: {}", e)); let script_file = &script_file; diff --git a/src/run_ts.rs b/src/run_ts.rs index 4c8d3e2..c978d80 100644 --- a/src/run_ts.rs +++ b/src/run_ts.rs @@ -26,7 +26,7 @@ pub fn do_run_script(args: &RunScriptArgs) { } (&args.arguments[args.arguments.len() - 1], false) })(); - let script_file = resolve_file(raw_script_file) + let script_file = resolve_file(raw_script_file, args.force_update) .unwrap_or_else(|e| failure_and_exit!("Failed to resolve script: {}", e)); let script_file = &script_file; verify::verify_script(script_file, is_env_on("RUNTS_SKIP_VERIFY"));