Skip to content

Commit 21cfc72

Browse files
authored
Merge pull request #133 from RafaelJohn9/feat/saving-with-different-name
Feat/saving with different name
2 parents 62f2bb6 + 2868a36 commit 21cfc72

8 files changed

Lines changed: 660 additions & 84 deletions

File tree

src/commands/gitignore/add.rs

Lines changed: 192 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ pub struct AddArgs {
3737
/// Update the gitignore cache
3838
#[arg(long = "update-cache", default_value = "false")]
3939
pub update_cache: bool,
40+
41+
/// Use the remote template file name as the output file name
42+
#[arg(long = "use-remote-name", short = 'n', default_value_t = false)]
43+
pub use_remote_name: bool,
44+
45+
/// Output file name (default: .gitignore)
46+
#[arg(
47+
long = "output",
48+
short = 'o',
49+
value_name = "FILENAME",
50+
num_args = 1.., // Accepts one or more values
51+
default_value = ".gitignore",
52+
requires = "templates"
53+
)]
54+
pub output: Vec<String>,
4055
}
4156

4257
impl super::Runnable for AddArgs {
@@ -51,7 +66,7 @@ impl super::Runnable for AddArgs {
5166
};
5267

5368
if self.all {
54-
download_all_templates(dir.as_ref(), self.force, &cache)?;
69+
download_all_templates(dir.as_ref(), self.force, &cache, self.use_remote_name)?;
5570
} else if self.templates.is_empty() {
5671
return Err(anyhow::anyhow!(
5772
"No gitignore template specified. Use `--all` or pass template names."
@@ -60,9 +75,11 @@ impl super::Runnable for AddArgs {
6075
download_templates(
6176
&self.templates,
6277
dir.as_ref(),
78+
&self.output,
6379
self.force,
6480
&cache,
6581
self.append,
82+
self.use_remote_name,
6683
)?;
6784
}
6885

@@ -74,83 +91,207 @@ fn download_all_templates(
7491
dir_path: Option<&PathBuf>,
7592
force: bool,
7693
cache: &Cache<String>,
94+
use_remote_name: bool,
7795
) -> Result<()> {
7896
println!("Fetching all gitignore templates...");
97+
let fetcher = Fetcher::new();
7998

80-
let dest_path = dir_path
81-
.map(|p| p.join(".gitignore"))
82-
.unwrap_or_else(|| Path::new(OUTPUT_BASE_PATH).join(OUTPUT).join(".gitignore"));
99+
if use_remote_name {
100+
// Save each template as its remote filename (e.g., Python.gitignore)
101+
for (_key, rel_path_entry) in cache.entries.iter() {
102+
let url = format!("{}/{}", GITHUB_RAW_BASE, rel_path_entry.data);
83103

84-
if force && dest_path.exists() {
85-
std::fs::remove_file(&dest_path)?;
86-
}
104+
// Extract remote filename from rel_path_entry.data
105+
let remote_filename = Path::new(&rel_path_entry.data)
106+
.file_name()
107+
.and_then(|s| s.to_str())
108+
.ok_or_else(|| anyhow::anyhow!("Invalid template path: {}", rel_path_entry.data))?;
87109

88-
for (key, rel_path_entry) in cache.entries.iter() {
89-
let fetcher = Fetcher::new();
90-
let url = format!("{}/{}", GITHUB_RAW_BASE, rel_path_entry.data);
110+
let dest_path = dir_path
111+
.map(|p| p.join(remote_filename))
112+
.unwrap_or_else(|| {
113+
Path::new(OUTPUT_BASE_PATH)
114+
.join(OUTPUT)
115+
.join(remote_filename)
116+
});
91117

92-
let msg = format!("Downloading gitignore template: {}", key);
93-
let pb = progress::spinner(&msg);
94-
let content = fetcher.fetch_content(&url)?;
95-
pb.set_message("Download Complete");
96-
pb.finish_and_clear();
118+
if force && dest_path.exists() {
119+
std::fs::remove_file(&dest_path)?;
120+
}
97121

98-
let section = format!("# ===== {}.gitignore =====\n{}\n\n", key, content);
99-
file::append_file(&section, &dest_path, None)?;
100-
}
122+
let msg = format!("Downloading gitignore template: {}", remote_filename);
123+
let pb = progress::spinner(&msg);
124+
let content = fetcher.fetch_content(&url)?;
125+
pb.set_message("Download Complete");
126+
pb.finish_and_clear();
127+
128+
let section = format!("# ===== {} =====\n{}\n\n", remote_filename, content);
129+
file::save_file(&section, &dest_path, force)?;
130+
131+
println!(
132+
"{} Downloaded gitignore template: {} to {}",
133+
"✓".green(),
134+
remote_filename,
135+
dest_path.display()
136+
);
137+
}
138+
} else {
139+
// Merge all templates into a single .gitignore file
140+
let dest_path = dir_path
141+
.map(|p| p.join(".gitignore"))
142+
.unwrap_or_else(|| Path::new(OUTPUT_BASE_PATH).join(OUTPUT).join(".gitignore"));
143+
144+
if force && dest_path.exists() {
145+
std::fs::remove_file(&dest_path)?;
146+
}
101147

102-
println!(
103-
"{} Downloaded and merged all gitignore templates to {}",
104-
"✓".green(),
105-
dest_path.display()
106-
);
148+
for (key, rel_path_entry) in cache.entries.iter() {
149+
let url = format!("{}/{}", GITHUB_RAW_BASE, rel_path_entry.data);
150+
151+
let msg = format!("Downloading gitignore template: {}", key);
152+
let pb = progress::spinner(&msg);
153+
let content = fetcher.fetch_content(&url)?;
154+
pb.set_message("Download Complete");
155+
pb.finish_and_clear();
156+
157+
let section = format!("# ===== {}.gitignore =====\n{}\n\n", key, content);
158+
file::append_file(&section, &dest_path, None)?;
159+
}
160+
161+
println!(
162+
"{} Downloaded and merged all gitignore templates to {}",
163+
"✓".green(),
164+
dest_path.display()
165+
);
166+
}
107167

108168
Ok(())
109169
}
110170

111171
fn download_templates(
112172
templates: &[String],
113173
dir_path: Option<&PathBuf>,
174+
output: &[String],
114175
force: bool,
115176
cache: &Cache<String>,
116177
append: bool,
178+
use_remote_name: bool,
117179
) -> Result<()> {
118-
let mut merged_content = String::new();
180+
let fetcher = Fetcher::new(); // Create once, reuse
119181

120-
for template_name in templates {
121-
// Try to find the template in cache with different key formats
122-
let template_path = find_template_in_cache(template_name, cache)?;
182+
if use_remote_name {
183+
// Each template is saved using its remote filename (e.g., Python.gitignore)
184+
for template_name in templates {
185+
let template_path = find_template_in_cache(template_name, cache)?;
186+
let url = format!("{}/{}", GITHUB_RAW_BASE, template_path);
123187

124-
let fetcher = Fetcher::new();
125-
let url = format!("{}/{}", GITHUB_RAW_BASE, template_path);
188+
// Extract filename from template_path (e.g., "Python.gitignore")
189+
let remote_filename = Path::new(&template_path)
190+
.file_name()
191+
.and_then(|s| s.to_str())
192+
.ok_or_else(|| anyhow::anyhow!("Invalid template path: {}", template_path))?;
126193

127-
let msg = format!("Downloading gitignore template: {}", template_name);
128-
let pb = progress::spinner(&msg);
129-
let content = fetcher.fetch_content(&url)?;
130-
pb.set_message("Download Complete");
131-
pb.finish_and_clear();
194+
let msg = format!("Downloading gitignore template: {}", template_name);
195+
let pb = progress::spinner(&msg);
196+
let content = fetcher.fetch_content(&url)?;
197+
pb.set_message("Download Complete");
198+
pb.finish_and_clear();
132199

133-
merged_content.push_str(&format!(
134-
"# ===== {}.gitignore =====\n{}\n\n",
135-
template_name, content
136-
));
137-
}
200+
let section = format!("# ===== {} =====\n{}\n\n", remote_filename, content);
138201

139-
let dest_path = dir_path
140-
.map(|p| p.join(".gitignore"))
141-
.unwrap_or_else(|| Path::new(OUTPUT_BASE_PATH).join(OUTPUT).join(".gitignore"));
202+
let dest_path = dir_path
203+
.map(|p| p.join(remote_filename))
204+
.unwrap_or_else(|| {
205+
Path::new(OUTPUT_BASE_PATH)
206+
.join(OUTPUT)
207+
.join(remote_filename)
208+
});
142209

143-
if append {
144-
file::append_file(&merged_content, &dest_path, None)?;
210+
if append {
211+
file::append_file(&section, &dest_path, None)?;
212+
} else {
213+
file::save_file(&section, &dest_path, force)?;
214+
}
215+
216+
println!(
217+
"{} Added gitignore template: {} to {}",
218+
"✓".green(),
219+
template_name,
220+
dest_path.display()
221+
);
222+
}
223+
} else if output.len() == templates.len() {
224+
// Save each template to its own file as specified in output
225+
for (template_name, output_file) in templates.iter().zip(output.iter()) {
226+
let template_path = find_template_in_cache(template_name, cache)?;
227+
let url = format!("{}/{}", GITHUB_RAW_BASE, template_path);
228+
229+
let msg = format!("Downloading gitignore template: {}", template_name);
230+
let pb = progress::spinner(&msg);
231+
let content = fetcher.fetch_content(&url)?;
232+
pb.set_message("Download Complete");
233+
pb.finish_and_clear();
234+
235+
let section = format!("# ===== {}.gitignore =====\n{}\n\n", template_name, content);
236+
237+
let dest_path = dir_path
238+
.map(|p| p.join(output_file))
239+
.unwrap_or_else(|| Path::new(OUTPUT_BASE_PATH).join(OUTPUT).join(output_file));
240+
241+
if append {
242+
file::append_file(&section, &dest_path, None)?;
243+
} else {
244+
file::save_file(&section, &dest_path, force)?;
245+
}
246+
247+
println!(
248+
"{} Added gitignore template: {} to {}",
249+
"✓".green(),
250+
template_name,
251+
dest_path.display()
252+
);
253+
}
254+
} else if output.len() == 1 {
255+
// Merge all templates into one file
256+
let mut merged_content = String::new();
257+
258+
for template_name in templates {
259+
let template_path = find_template_in_cache(template_name, cache)?;
260+
let url = format!("{}/{}", GITHUB_RAW_BASE, template_path);
261+
262+
let msg = format!("Downloading gitignore template: {}", template_name);
263+
let pb = progress::spinner(&msg);
264+
let content = fetcher.fetch_content(&url)?;
265+
pb.set_message("Download Complete");
266+
pb.finish_and_clear();
267+
268+
merged_content.push_str(&format!(
269+
"# ===== {}.gitignore =====\n{}\n\n",
270+
template_name, content
271+
));
272+
}
273+
274+
let dest_path = dir_path
275+
.map(|p| p.join(&output[0]))
276+
.unwrap_or_else(|| Path::new(OUTPUT_BASE_PATH).join(OUTPUT).join(&output[0]));
277+
278+
if append {
279+
file::append_file(&merged_content, &dest_path, None)?;
280+
} else {
281+
file::save_file(&merged_content, &dest_path, force)?;
282+
}
283+
284+
println!(
285+
"{} Added gitignore templates: {} to {}",
286+
"✓".green(),
287+
templates.join(", "),
288+
dest_path.display()
289+
);
145290
} else {
146-
file::save_file(&merged_content, &dest_path, force)?;
291+
return Err(anyhow::anyhow!(
292+
"Number of output files must be either 1 or match the number of templates when not using --use-remote-name"
293+
));
147294
}
148295

149-
println!(
150-
"{} Added gitignore templates: {}",
151-
"✓".green(),
152-
templates.join(", ")
153-
);
154-
155296
Ok(())
156297
}

src/commands/issue/add.rs

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ pub struct AddArgs {
3030
/// Download all available templates
3131
#[arg(long)]
3232
pub all: bool,
33+
34+
/// Output file names for the templates (in order of templates)
35+
#[arg(short='o', long, value_name = "OUTPUT", num_args = 1.., requires = "templates")]
36+
pub output: Vec<String>,
3337
}
3438

3539
impl super::Runnable for AddArgs {
@@ -41,8 +45,24 @@ impl super::Runnable for AddArgs {
4145
"No issue template specified. Use `--all` or pass template names."
4246
));
4347
} else {
44-
for template_name in &self.templates {
45-
download_single_template(template_name, self.dir.as_ref(), self.force)?;
48+
if !self.output.is_empty() {
49+
if self.templates.len() != self.output.len() {
50+
return Err(anyhow::anyhow!(
51+
"The number of templates and output file names must match."
52+
));
53+
}
54+
for (template_name, output_name) in self.templates.iter().zip(self.output.iter()) {
55+
download_single_template(
56+
template_name,
57+
self.dir.as_ref(),
58+
self.force,
59+
Some(output_name.clone()),
60+
)?;
61+
}
62+
} else {
63+
for template_name in &self.templates {
64+
download_single_template(template_name, self.dir.as_ref(), self.force, None)?;
65+
}
4666
}
4767
}
4868

@@ -65,7 +85,7 @@ fn download_all_templates(dir_path: Option<&PathBuf>, force: bool) -> anyhow::Re
6585
None => &entry.name,
6686
};
6787

68-
if let Err(e) = download_single_template(template_name, dir_path, force) {
88+
if let Err(e) = download_single_template(template_name, dir_path, force, None) {
6989
eprintln!(
7090
"{} Failed to add template '{}': {}",
7191
"✗".red(),
@@ -101,9 +121,9 @@ fn download_single_template(
101121
template_name: &str,
102122
dir_path: Option<&PathBuf>,
103123
force: bool,
124+
output: Option<String>,
104125
) -> anyhow::Result<()> {
105126
let fetcher = Fetcher::new();
106-
107127
let url = format!("{}/issue-templates/{}.yml", GITHUB_RAW_BASE, template_name);
108128

109129
let msg = format!("Downloading issue template: {}", template_name);
@@ -112,13 +132,31 @@ fn download_single_template(
112132
pb.set_message("Download Complete");
113133
pb.finish_and_clear();
114134

115-
let default_path = Path::new(OUTPUT_BASE_PATH).join(OUTPUT);
116-
let base_path = dir_path.map_or(default_path.as_path(), |p| p.as_path());
117-
let dest_path = base_path.join(format!("{}.yml", template_name));
135+
// Determine output path logic
136+
let dest_path = if let Some(mut output_file) = output {
137+
// Technical Debt: Add .yml extension if not present. Not flexible for other formats.
138+
if Path::new(&output_file).extension().is_none() {
139+
output_file.push_str(".yml");
140+
}
141+
dir_path
142+
.map(|p| p.join(&output_file))
143+
.unwrap_or_else(|| Path::new(OUTPUT_BASE_PATH).join(OUTPUT).join(&output_file))
144+
} else {
145+
// Default: .github/ISSUE_TEMPLATE/<template_name>.yml
146+
let default_path = Path::new(OUTPUT_BASE_PATH).join(OUTPUT);
147+
dir_path
148+
.map(|p| p.join(format!("{}.yml", template_name)))
149+
.unwrap_or_else(|| default_path.join(format!("{}.yml", template_name)))
150+
};
118151

119152
file::save_file(&content, &dest_path, force)?;
120153

121-
println!("{} Added template: {}", "✓".green(), template_name);
154+
println!(
155+
"{} Added template: {} to {}",
156+
"✓".green(),
157+
template_name,
158+
dest_path.display()
159+
);
122160

123161
Ok(())
124162
}

0 commit comments

Comments
 (0)