use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;
use chrono::NaiveDate;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use super::fs::FsCleaner;
use super::fs::canonicalize_path;
use super::progress_bar::ProgressBar;
pub struct TempNodeModulesDir {
node_modules_dir_path: PathBuf,
_temp_dir: tempfile::TempDir,
}
impl TempNodeModulesDir {
pub fn parent(&self) -> &Path {
self.node_modules_dir_path.parent().unwrap()
}
pub fn node_modules_dir_path(&self) -> &Path {
&self.node_modules_dir_path
}
}
pub fn create_temp_node_modules_dir() -> Result<TempNodeModulesDir, AnyError> {
let root_temp_folder = std::env::temp_dir().join("deno_nm");
let today = chrono::Utc::now().date_naive();
if let Err(err) =
attempt_temp_dir_garbage_collection(&root_temp_folder, today)
{
log::debug!("Failed init temp folder garbage collection: {:#?}", err);
}
let day_folder = root_temp_folder.join(folder_name_for_date(today));
std::fs::create_dir_all(&day_folder)
.with_context(|| format!("Failed creating '{}'", day_folder.display()))?;
let temp_node_modules_parent_dir = tempfile::TempDir::new_in(&day_folder)?;
let package_json_path =
temp_node_modules_parent_dir.path().join("package.json");
std::fs::write(&package_json_path, "{}").with_context(|| {
format!("Failed creating '{}'", package_json_path.display())
})?;
let temp_dir_path = canonicalize_path(temp_node_modules_parent_dir.path())
.unwrap_or_else(|_| temp_node_modules_parent_dir.path().to_path_buf());
let node_modules_dir_path = temp_dir_path.join("node_modules");
log::debug!(
"Creating node_modules directory at: {}",
node_modules_dir_path.display()
);
Ok(TempNodeModulesDir {
node_modules_dir_path,
_temp_dir: temp_node_modules_parent_dir,
})
}
fn attempt_temp_dir_garbage_collection(
root_temp_folder: &Path,
utc_now: NaiveDate,
) -> Result<(), AnyError> {
let previous_day_str = folder_name_for_date(
utc_now
.checked_sub_days(chrono::Days::new(1))
.unwrap_or(utc_now),
);
let current_day_str = folder_name_for_date(utc_now);
let next_day_str = folder_name_for_date(
utc_now
.checked_add_days(chrono::Days::new(1))
.unwrap_or(utc_now),
);
let progress_bar =
ProgressBar::new(crate::util::progress_bar::ProgressBarStyle::TextOnly);
let update_guard = progress_bar.deferred_update_with_prompt(
crate::util::progress_bar::ProgressMessagePrompt::Cleaning,
"old temp node_modules folders...",
);
let mut cleaner = FsCleaner::new(Some(update_guard));
for entry in std::fs::read_dir(root_temp_folder)? {
let Ok(entry) = entry else {
continue;
};
if entry.file_name() != previous_day_str
&& entry.file_name() != current_day_str
&& entry.file_name() != next_day_str
&& let Err(err) = cleaner.rm_rf(&entry.path())
{
log::debug!(
"Failed cleaning '{}': {:#?}",
entry.file_name().display(),
err
);
}
}
Ok(())
}
fn folder_name_for_date(date: chrono::NaiveDate) -> OsString {
OsString::from(date.format("%Y-%m-%d").to_string())
}
#[cfg(test)]
mod test {
use test_util::TempDir;
use super::*;
#[test]
fn test_attempt_temp_dir_garbage_collection() {
let temp_dir = TempDir::new();
let reference_date = chrono::NaiveDate::from_ymd_opt(2020, 5, 13).unwrap();
temp_dir.path().join("0000-00-00").create_dir_all();
temp_dir
.path()
.join("2020-05-01/sub_dir/sub")
.create_dir_all();
temp_dir
.path()
.join("2020-05-01/sub_dir/sub/test.txt")
.write("");
temp_dir.path().join("2020-05-02/sub_dir").create_dir_all();
temp_dir.path().join("2020-05-11").create_dir_all();
temp_dir.path().join("2020-05-12").create_dir_all();
temp_dir.path().join("2020-05-13").create_dir_all();
temp_dir.path().join("2020-05-14").create_dir_all();
temp_dir.path().join("2020-05-15").create_dir_all();
attempt_temp_dir_garbage_collection(
temp_dir.path().as_path(),
reference_date,
)
.unwrap();
let mut entries = std::fs::read_dir(temp_dir.path())
.unwrap()
.map(|e| e.unwrap().file_name().into_string().unwrap())
.collect::<Vec<_>>();
entries.sort();
assert_eq!(
entries,
vec![
"2020-05-12".to_string(),
"2020-05-13".to_string(),
"2020-05-14".to_string()
]
);
}
}