9
9
//! create-rust-github-repo --name my-new-project
10
10
//!
11
11
//! # Copy configs from existing project
12
- //! create-rust-github-repo --name my-new-project --copy-configs-from ~/workspace/my-existing-project
12
+ //! create-rust-github-repo --name my-new-project --copy-configs-from ~/workspace/my-existing-project --configs .github,rustfmt.toml,clippy.toml
13
13
//!
14
14
//! # Clone to a specific directory
15
15
//! create-rust-github-repo --name my-new-project --dir ~/workspace/my-new-project
@@ -38,6 +38,8 @@ use std::process::{Command, ExitStatus};
38
38
use anyhow:: Context ;
39
39
use clap:: { value_parser, Parser , ValueEnum } ;
40
40
use derive_setters:: Setters ;
41
+ use fs_extra:: copy_items;
42
+ use fs_extra:: dir:: CopyOptions ;
41
43
42
44
#[ derive( ValueEnum , Default , Eq , PartialEq , Hash , Clone , Copy , Debug ) ]
43
45
pub enum RepoVisibility {
@@ -73,11 +75,12 @@ pub struct CreateRustGithubRepo {
73
75
#[ arg( long, help = "Shell to use for executing commands" , default_value = "/bin/sh" ) ]
74
76
shell_cmd : String ,
75
77
76
- #[ arg( long, short, help = "Source directory for configuration files " , value_parser = value_parser!( PathBuf ) ) ]
78
+ #[ arg( long, short, help = "Source directory for config paths " , value_parser = value_parser!( PathBuf ) ) ]
77
79
copy_configs_from : Option < PathBuf > ,
78
80
79
- #[ arg( long, help = "Extra config file paths (relative to resolved `dir`), separated by comma" , value_delimiter = ',' ) ]
80
- extra_configs : Vec < String > ,
81
+ /// Config paths separated by comma (relative to `copy_configs_from`) (only applies if `copy_configs_from` is specified) (supports files and directories)
82
+ #[ arg( long, value_delimiter = ',' ) ]
83
+ configs : Vec < String > ,
81
84
82
85
#[ arg( long, help = "Shell command to check if repo exists (supports substitutions - see help below)" , default_value = "gh repo view --json nameWithOwner {{name}} 2>/dev/null" ) ]
83
86
repo_exists_cmd : String ,
@@ -97,7 +100,7 @@ pub struct CreateRustGithubRepo {
97
100
#[ arg( long, help = "Shell command to add new files (supports substitutions - see help below)" , default_value = "git add ." ) ]
98
101
repo_add_args : String ,
99
102
100
- #[ arg( long, help = "Shell command to make a commit (supports substitutions - see help below)" , default_value = "git commit -m \" Add configs \" " ) ]
103
+ #[ arg( long, help = "Shell command to make a commit (supports substitutions - see help below)" , default_value = "git commit -m \" Setup project \" " ) ]
101
104
repo_commit_args : String ,
102
105
103
106
#[ arg( long, help = "Shell command to push the commit (supports substitutions - see help below)" , default_value = "git push" ) ]
@@ -142,11 +145,16 @@ impl CreateRustGithubRepo {
142
145
}
143
146
144
147
if let Some ( copy_configs_from) = self . copy_configs_from {
145
- let mut configs: Vec < String > = vec ! [ ] ;
146
- configs. extend ( CONFIGS . iter ( ) . copied ( ) . map ( ToOwned :: to_owned) ) ;
147
- configs. extend ( self . extra_configs ) ;
148
- // Copy config files
149
- copy_configs_if_not_exists ( & copy_configs_from, & dir, configs) . context ( "Failed to copy configuration files" ) ?;
148
+ let paths: Vec < PathBuf > = self
149
+ . configs
150
+ . iter ( )
151
+ . map ( |config| copy_configs_from. join ( config) )
152
+ . collect ( ) ;
153
+ let options = CopyOptions :: new ( )
154
+ . skip_exist ( true )
155
+ . copy_inside ( true )
156
+ . buffer_size ( MEGABYTE ) ;
157
+ copy_items ( & paths, & dir, & options) . context ( "Failed to copy configuration files" ) ?;
150
158
}
151
159
152
160
// test
@@ -178,27 +186,28 @@ pub fn replace_all(mut input: String, substitutions: &HashMap<&str, &str>) -> St
178
186
input
179
187
}
180
188
181
- pub fn exec ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > , extra_args : impl IntoIterator < Item = String > , current_dir : impl AsRef < Path > , substitutions : & HashMap < & str , & str > ) -> io:: Result < ExitStatus > {
189
+ pub fn exec ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > + Clone , extra_args : impl IntoIterator < Item = String > , current_dir : impl AsRef < Path > , substitutions : & HashMap < & str , & str > ) -> io:: Result < ExitStatus > {
182
190
let replacements = replace_args ( extra_args, substitutions) ;
183
191
let extra_args = replacements. iter ( ) . map ( AsRef :: < OsStr > :: as_ref) ;
184
192
exec_raw ( cmd, args, extra_args, current_dir)
185
193
}
186
194
187
- pub fn success ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > , extra_args : impl IntoIterator < Item = String > , current_dir : impl AsRef < Path > , substitutions : & HashMap < & str , & str > ) -> io:: Result < bool > {
195
+ pub fn success ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > + Clone , extra_args : impl IntoIterator < Item = String > , current_dir : impl AsRef < Path > , substitutions : & HashMap < & str , & str > ) -> io:: Result < bool > {
188
196
let replacements = replace_args ( extra_args, substitutions) ;
189
197
let extra_args = replacements. iter ( ) . map ( AsRef :: < OsStr > :: as_ref) ;
190
198
success_raw ( cmd, args, extra_args, current_dir)
191
199
}
192
200
193
- pub fn exec_raw ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > , extra_args : impl IntoIterator < Item = impl AsRef < OsStr > > , current_dir : impl AsRef < Path > ) -> io:: Result < ExitStatus > {
201
+ pub fn exec_raw ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > + Clone , extra_args : impl IntoIterator < Item = impl AsRef < OsStr > > , current_dir : impl AsRef < Path > ) -> io:: Result < ExitStatus > {
194
202
get_status_raw ( cmd, args, extra_args, current_dir) . and_then ( check_status)
195
203
}
196
204
197
- pub fn success_raw ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > , extra_args : impl IntoIterator < Item = impl AsRef < OsStr > > , current_dir : impl AsRef < Path > ) -> io:: Result < bool > {
205
+ pub fn success_raw ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > + Clone , extra_args : impl IntoIterator < Item = impl AsRef < OsStr > > , current_dir : impl AsRef < Path > ) -> io:: Result < bool > {
198
206
get_status_raw ( cmd, args, extra_args, current_dir) . map ( |status| status. success ( ) )
199
207
}
200
208
201
- pub fn get_status_raw ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > , extra_args : impl IntoIterator < Item = impl AsRef < OsStr > > , current_dir : impl AsRef < Path > ) -> io:: Result < ExitStatus > {
209
+ pub fn get_status_raw ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > + Clone , extra_args : impl IntoIterator < Item = impl AsRef < OsStr > > , current_dir : impl AsRef < Path > ) -> io:: Result < ExitStatus > {
210
+ eprintln ! ( "$ {}" , cmd_to_string( cmd. as_ref( ) , args. clone( ) ) ) ;
202
211
Command :: new ( cmd)
203
212
. args ( args)
204
213
. args ( extra_args)
@@ -207,6 +216,15 @@ pub fn get_status_raw(cmd: impl AsRef<OsStr>, args: impl IntoIterator<Item = imp
207
216
. wait ( )
208
217
}
209
218
219
+ fn cmd_to_string ( cmd : impl AsRef < OsStr > , args : impl IntoIterator < Item = impl AsRef < OsStr > > ) -> String {
220
+ let mut cmd_str = cmd. as_ref ( ) . to_string_lossy ( ) . to_string ( ) ;
221
+ for arg in args {
222
+ cmd_str. push ( ' ' ) ;
223
+ cmd_str. push_str ( arg. as_ref ( ) . to_string_lossy ( ) . as_ref ( ) ) ;
224
+ }
225
+ cmd_str
226
+ }
227
+
210
228
pub fn check_status ( status : ExitStatus ) -> io:: Result < ExitStatus > {
211
229
if status. success ( ) {
212
230
Ok ( status)
@@ -215,33 +233,10 @@ pub fn check_status(status: ExitStatus) -> io::Result<ExitStatus> {
215
233
}
216
234
}
217
235
218
- pub fn copy_configs_if_not_exists < P : Clone + AsRef < Path > > ( source : & Path , target : & Path , configs : impl IntoIterator < Item = P > ) -> io:: Result < ( ) > {
219
- for config in configs {
220
- let source_path = source. join ( config. clone ( ) ) ;
221
- let target_path = target. join ( config) ;
222
- if source_path. exists ( ) && !target_path. exists ( ) {
223
- fs_err:: copy ( & source_path, & target_path) ?;
224
- }
225
- }
226
- Ok ( ( ) )
227
- }
228
-
229
- pub const CONFIGS : & [ & str ] = & [
230
- "clippy.toml" ,
231
- "rustfmt.toml" ,
232
- "Justfile" ,
233
- "lefthook.yml" ,
234
- ".lefthook.yml" ,
235
- "lefthook.yaml" ,
236
- ".lefthook.yaml" ,
237
- "lefthook.toml" ,
238
- ".lefthook.toml" ,
239
- "lefthook.json" ,
240
- ".lefthook.json" ,
241
- ] ;
242
-
243
236
#[ test]
244
237
fn verify_cli ( ) {
245
238
use clap:: CommandFactory ;
246
239
CreateRustGithubRepo :: command ( ) . debug_assert ( ) ;
247
240
}
241
+
242
+ const MEGABYTE : usize = 1048576 ;
0 commit comments