diff --git a/script_examples/super_add/src/main.rs b/script_examples/super_add/src/main.rs index 486219d4..df35e9af 100644 --- a/script_examples/super_add/src/main.rs +++ b/script_examples/super_add/src/main.rs @@ -1,60 +1,98 @@ use irust_api::{Command, OutputEvent, Shutdown}; use rscript::scripting::Scripter; use rscript::{Hook, VersionReq}; +use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; use std::env; use std::fs; -use std::path::{Path, PathBuf}; -use std::time::{SystemTime, UNIX_EPOCH}; -use syn::{File, Item, Visibility, visit_mut::VisitMut}; +use std::hash::{Hash, Hasher}; +use std::path::{absolute, Path, PathBuf}; +use std::process; +use syn::{visit_mut::VisitMut, File, Item, Visibility}; + +fn hash(s: &str) -> u64 { + let mut hasher = DefaultHasher::new(); + s.hash(&mut hasher); + hasher.finish() +} + +fn get_temp_path(crate_src: &Path) -> PathBuf { + env::temp_dir().join(format!( + "irust_super_add_{}", + hash(&crate_src.to_string_lossy()) + )) +} + +fn get_actual_cargo_root(crate_src: &Path) -> Option { + env::set_current_dir(crate_src).ok()?; + let root_toml = process::Command::new("cargo") + .args(["locate-project", "--workspace", "--message-format=plain"]) + .output() + .map(|o| String::from_utf8_lossy(&o.stdout).into_owned()) + .ok()?; + Some(PathBuf::from(&root_toml.trim_end_matches("/Cargo.toml\n"))) +} #[derive(Debug)] struct TempCrateModifier { temp_path: PathBuf, + copied_root: PathBuf, } impl TempCrateModifier { /// Create a new temporary copy of the crate with all items made public fn new(source_path: &Path) -> Result> { - let temp_path = create_temp_copy(source_path)?; + let (copied_root, temp_path) = match get_actual_cargo_root(source_path) { + Some(root) => { + let copied_root = create_temp_copy(&root)?; + ( + copied_root.clone(), + copied_root.join(source_path.strip_prefix(&root).expect(&format!( + "Cargo returned a non parent actual path: {}, {}", + source_path.display(), + root.display(), + ))), + ) + } + None => { + let copied_root = create_temp_copy(source_path)?; + (copied_root.clone(), copied_root) + } + }; + make_all_items_public(&temp_path)?; - Ok(TempCrateModifier { temp_path }) + Ok(TempCrateModifier { + temp_path, + copied_root, + }) } /// Get the path to the temporary crate fn path(&self) -> &Path { &self.temp_path } -} -impl Drop for TempCrateModifier { - fn drop(&mut self) { - let _ = fs::remove_dir_all(&self.temp_path); + fn clear(&mut self) { + let _ = fs::remove_dir_all(&self.copied_root); } } fn create_temp_copy(source: &Path) -> Result> { - let temp_base = env::temp_dir(); - let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos(); - let temp_name = format!("irust_super_add_{}", timestamp); - let temp_path = temp_base.join(temp_name); - - fs::create_dir_all(&temp_path)?; + let temp_path = get_temp_path(source); copy_dir_recursive(source, &temp_path)?; Ok(temp_path) } fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), Box> { - if !dst.exists() { - fs::create_dir_all(dst)?; - } - + fs::create_dir_all(dst)?; for entry in fs::read_dir(src)? { let entry = entry?; let src_path = entry.path(); let dst_path = dst.join(entry.file_name()); + let src_metadata = fs::symlink_metadata(&src_path)?; if src_path.is_dir() { // Skip common directories that shouldn't be copied if let Some(dir_name) = src_path.file_name() { @@ -68,15 +106,29 @@ fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), Box Result<(), Box> { - visit_rust_files(dir, &mut |file_path| process_rust_file(file_path)) + visit_rust_files(&dir.join("src"), &mut |file_path| { + process_rust_file(file_path) + }) } fn visit_rust_files(dir: &Path, callback: &mut F) -> Result<(), Box> @@ -191,7 +243,7 @@ impl VisitMut for PublicityTransformer { #[derive(Debug, Default)] struct SuperAdd { - temp_crates: Vec, + temp_crates: HashMap, } impl Scripter for SuperAdd { @@ -233,9 +285,11 @@ impl SuperAdd { }; if !path_str.is_empty() { - let source_path = PathBuf::from(path_str); + let maybe_source_path = absolute(PathBuf::from(path_str)); - if source_path.exists() { + if let Ok(source_path) = maybe_source_path + && source_path.exists() + { match TempCrateModifier::new(&source_path) { Ok(temp_crate) => { let temp_path = temp_crate.path().to_string_lossy().to_string(); @@ -244,7 +298,7 @@ impl SuperAdd { let modified_command = input.replace(path_str, &temp_path); // Store the temp crate to keep it alive - self.temp_crates.push(temp_crate); + self.temp_crates.insert(temp_path, temp_crate); // Send the modified command let cmd = Command::Parse(modified_command); @@ -264,13 +318,13 @@ impl SuperAdd { // If we get here, either it's not an :add --path command or something went wrong Self::write::(&None); } - Shutdown::NAME => { // Clean up all temp crates - self.temp_crates.clear(); + for crat in self.temp_crates.values_mut() { + crat.clear() + } Self::write::(&None); } - _ => unreachable!(), } }