@@ -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
4257impl 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
111171fn 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}
0 commit comments