1use crate::error::{Result, ToriiError};
18use std::path::{Path, PathBuf};
19use std::process::Command;
20
21#[derive(Debug, Default)]
24pub struct ExportOpts {
25 pub output_dir: Option<PathBuf>,
27 pub stdout: bool,
29 pub cover_letter: bool,
31}
32
33pub fn export(repo_path: &Path, range: &str, opts: &ExportOpts) -> Result<()> {
34 let mut args = vec!["format-patch".to_string()];
35 if let Some(dir) = &opts.output_dir {
36 args.push("-o".to_string());
37 args.push(dir.to_string_lossy().to_string());
38 }
39 if opts.stdout {
40 args.push("--stdout".to_string());
41 }
42 if opts.cover_letter {
43 args.push("--cover-letter".to_string());
44 }
45 args.push(range.to_string());
46
47 println!("📨 patch export range={range}");
48 let status = Command::new("git")
49 .args(&args)
50 .current_dir(repo_path)
51 .status()
52 .map_err(|e| ToriiError::Subprocess { tool: "git".into(), message: format!("invoke git format-patch: {e}") })?;
53 if !status.success() {
54 return Err(ToriiError::Subprocess { tool: "git".into(), message: format!(
55 "git format-patch exited with {status}"
56 ) });
57 }
58 Ok(())
59}
60
61#[derive(Debug, Default)]
64pub struct ApplyOpts {
65 pub three_way: bool,
67 pub abort: bool,
69 pub continue_: bool,
71 pub skip: bool,
73}
74
75pub fn apply(repo_path: &Path, files: &[PathBuf], opts: &ApplyOpts) -> Result<()> {
76 let mut args = vec!["am".to_string()];
77 if opts.three_way {
78 args.push("--3way".to_string());
79 }
80 if opts.abort {
81 args.push("--abort".to_string());
82 } else if opts.continue_ {
83 args.push("--continue".to_string());
84 } else if opts.skip {
85 args.push("--skip".to_string());
86 } else {
87 if files.is_empty() {
88 return Err(ToriiError::Usage(
89 "patch apply needs at least one file, or --abort / --continue / --skip".into(),
90 ));
91 }
92 for f in files {
93 args.push(f.to_string_lossy().to_string());
94 }
95 }
96
97 println!("📨 patch apply ({} arg(s))", args.len() - 1);
98 let status = Command::new("git")
99 .args(&args)
100 .current_dir(repo_path)
101 .status()
102 .map_err(|e| ToriiError::Subprocess { tool: "git".into(), message: format!("invoke git am: {e}") })?;
103 if !status.success() {
104 return Err(ToriiError::Subprocess { tool: "git".into(), message: format!(
105 "git am exited with {status} — resolve and run `torii patch apply --continue`"
106 ) });
107 }
108 Ok(())
109}