use miette::{IntoDiagnostic, Result, WrapErr};
use workon::{
copy_untracked, get_repo, workon_root, CopyOptions, WorkonConfig, WorktreeDescriptor,
};
use crate::cli::Copy;
use crate::output;
use super::Run;
impl Run for Copy {
fn run(&self) -> Result<Option<WorktreeDescriptor>> {
let repo = get_repo(None)?;
let config = WorkonConfig::new(&repo)?;
let root = workon_root(&repo)?;
let to_name = self.to.as_deref().unwrap_or(".");
let from_path = resolve_worktree_arg(root, &self.from)?;
let to_path = resolve_worktree_arg(root, to_name)?;
if !from_path.exists() {
return Err(miette::miette!(
"Source worktree '{}' does not exist at {:?}",
self.from,
from_path
));
}
if !to_path.exists() {
return Err(miette::miette!(
"Destination worktree '{}' does not exist at {:?}",
to_name,
to_path
));
}
let patterns = determine_patterns(self, &config)?;
let excludes = determine_excludes(self, &config)?;
let include_ignored =
config.copy_include_ignored(self.no_include_ignored.then_some(false))?;
let json_mode = output::is_json_mode();
let pb = output::create_spinner();
pb.set_message("Copying files...");
let show_skipped = log::log_enabled!(log::Level::Trace);
let mut count = 0usize;
let pb_copied = pb.clone();
let pb_skipped = pb.clone();
let copied = copy_untracked(
&from_path,
&to_path,
CopyOptions {
patterns: &patterns,
excludes: &excludes,
force: self.force,
include_ignored,
on_copied: Box::new(move |rel_path| {
if !json_mode {
count += 1;
pb_copied.println(format!(
" {} {}",
output::style::green_bold("Copied"),
rel_path.display()
));
pb_copied.set_message(format!("Copying files... ({} copied)", count));
}
}),
on_skipped: Box::new(move |reason, rel_path| {
if show_skipped && !json_mode {
pb_skipped.println(format!(
" {} {} ({})",
output::style::dim("Skipped"),
rel_path.display(),
reason,
));
}
}),
},
)
.wrap_err(format!(
"Failed to copy files from '{}' to '{}'",
self.from, to_name
))?;
pb.finish_and_clear();
if !json_mode {
println!("\nCopied {} file(s)", copied.len());
}
Ok(WorktreeDescriptor::new(&repo, to_name).ok())
}
}
fn determine_patterns(cmd: &Copy, config: &WorkonConfig) -> Result<Vec<String>> {
if let Some(pattern) = &cmd.pattern {
return Ok(vec![pattern.clone()]);
}
Ok(config.copy_patterns()?)
}
fn resolve_worktree_arg(root: &std::path::Path, name: &str) -> Result<std::path::PathBuf> {
if name == "." {
let cwd = std::env::current_dir()
.into_diagnostic()
.wrap_err("Failed to get current directory")?;
if let Ok(rel) = cwd.strip_prefix(root) {
if let Some(first) = rel.components().next() {
return Ok(root.join(first));
}
}
return Ok(cwd);
}
Ok(root.join(name))
}
fn determine_excludes(cmd: &Copy, config: &WorkonConfig) -> Result<Vec<String>> {
let mut excludes = config.copy_excludes()?;
excludes.extend(cmd.exclude.iter().cloned());
Ok(excludes)
}