use super::action::Action;
use super::subvolume_command::main_checkout_root;
use super::subvolume_error::Interrupt;
use super::subvolume_index_files::linked_worktrees_inside;
use super::subvolume_platform::{LinuxPlatform, Platform};
use super::worktree_convert_arguments::{CONVERT_HELP, ConvertArguments};
use super::worktree_convert::{ConvertOptions, Verdict, convert_worktree};
use std::io::Write;
use std::path::{Path, PathBuf};
pub const REFUSED_EXIT_CODE: i32 = 1;
pub const FAILED_EXIT_CODE: i32 = 2;
fn targets(arguments: &ConvertArguments, cwd: &Path) -> Result<Vec<PathBuf>, String> {
if arguments.all {
let root: PathBuf = main_checkout_root(cwd)?;
return Ok(linked_worktrees_inside(root.as_path()).into_iter().map(|linked| { return linked.worktree; }).collect());
}
return Ok(arguments.paths.iter().map(|word: &std::ffi::OsString| { return cwd.join(word); }).collect());
}
pub fn execute(platform: &dyn Platform, arguments: &ConvertArguments, cwd: &Path, interrupt: &Interrupt, say: &mut dyn FnMut(&str)) -> i32 {
let list: Vec<PathBuf> = match targets(arguments, cwd) {
Ok(found) => found,
Err(reason) => {
say(format!("git cli-git convert-worktree-to-subvolume: refused: {reason}").as_str());
say("Nothing was changed.");
return REFUSED_EXIT_CODE;
}
};
let options: ConvertOptions = ConvertOptions { dry_run: arguments.dry_run, abandon: arguments.abandon };
let mut refused: usize = 0;
let mut converted: usize = 0;
for path in &list {
say(format!("{}", path.display()).as_str());
match convert_worktree(platform, path.as_path(), options, interrupt, &mut *say) {
Ok(Verdict::Converted) => {
converted += 1;
say(" converted into a subvolume");
}
Ok(Verdict::Resumed) => {
converted += 1;
say(if arguments.dry_run { " an unfinished conversion would be completed" } else { " the unfinished conversion was completed" });
}
Ok(Verdict::WouldConvert) => say(" would convert"),
Ok(Verdict::AlreadySubvolume) => say(" already a subvolume, skipped"),
Ok(Verdict::Abandoned) => say(" the unfinished conversion was undone"),
Ok(Verdict::Skipped(reasons)) => {
refused += 1;
for reason in reasons {
say(format!(" skipped: {reason}").as_str());
}
}
Err(error) => {
say(format!(" failed: {error}").as_str());
say(" The conversion is journaled. Run the same command again to finish it, or add --abandon to undo it before its swap.");
return FAILED_EXIT_CODE;
}
}
}
say(format!("{} worktree(s) looked at, {converted} converted or completed, {refused} skipped.", list.len()).as_str());
return if refused > 0 && !arguments.all && !arguments.dry_run { REFUSED_EXIT_CODE } else { 0 };
}
pub fn convert_action(arguments: &ConvertArguments, cwd: &Path) -> Action {
if arguments.help {
return Action::Exit { code: 0, stdout: String::from(CONVERT_HELP), stderr: String::new() };
}
let mut say = |text: &str| {
let mut stderr = std::io::stderr().lock();
let _written = writeln!(stderr, "{text}");
};
let code: i32 = execute(&LinuxPlatform, arguments, cwd, &Interrupt::never(), &mut say);
return Action::Exit { code, stdout: String::new(), stderr: String::new() };
}
#[cfg(test)]
#[path = "worktree_convert_command_tests.rs"]
mod tests;