Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 82 additions & 28 deletions script_examples/super_add/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf> {
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<Self, Box<dyn std::error::Error>> {
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<PathBuf, Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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() {
Expand All @@ -68,15 +106,29 @@ fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), Box<dyn std::error::
}
copy_dir_recursive(&src_path, &dst_path)?;
} else {
if src_metadata.is_symlink() {
if fs::metadata(&src_path).is_err() {
continue;
}
}
if dst_path.exists() {
if let Ok(time_src) = src_metadata.modified()
&& let Ok(time_dst) = fs::symlink_metadata(&dst_path)?.modified()
&& time_dst == time_src
{
continue;
}
}
fs::copy(&src_path, &dst_path)?;
}
}

Ok(())
}

fn make_all_items_public(dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
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<F>(dir: &Path, callback: &mut F) -> Result<(), Box<dyn std::error::Error>>
Expand Down Expand Up @@ -191,7 +243,7 @@ impl VisitMut for PublicityTransformer {

#[derive(Debug, Default)]
struct SuperAdd {
temp_crates: Vec<TempCrateModifier>,
temp_crates: HashMap<String, TempCrateModifier>,
}

impl Scripter for SuperAdd {
Expand Down Expand Up @@ -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();
Expand All @@ -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);
Expand All @@ -264,13 +318,13 @@ impl SuperAdd {
// If we get here, either it's not an :add --path command or something went wrong
Self::write::<OutputEvent>(&None);
}

Shutdown::NAME => {
// Clean up all temp crates
self.temp_crates.clear();
for crat in self.temp_crates.values_mut() {
crat.clear()
}
Self::write::<Shutdown>(&None);
}

_ => unreachable!(),
}
}
Expand Down
Loading