1
- use std:: ffi :: OsStr ;
1
+ use std:: path :: PathBuf ;
2
2
3
3
use crossterm:: style:: { Attribute , Stylize } ;
4
+ use git2:: { Cred , RemoteCallbacks , Repository } ;
4
5
use miette:: { Diagnostic , Result } ;
5
6
use tap:: Pipe ;
6
7
#[ cfg( feature = "profiling" ) ]
7
8
use tracing:: instrument;
8
9
9
10
use super :: Command ;
10
- use crate :: {
11
- config:: { self , Config } ,
12
- helpers,
13
- } ;
11
+ use crate :: config:: { self , Config } ;
14
12
15
13
#[ derive( thiserror:: Error , Diagnostic , Debug ) ]
16
14
enum Error {
17
- #[ error( "Clone command did not run successfully" ) ]
18
- #[ diagnostic( code( clone:: command:: run) ) ]
19
- CloneExecute ( #[ from] helpers:: RunError ) ,
15
+ #[ error( "Could not create dotfiles directory \" {0}\" " ) ]
16
+ #[ diagnostic( code( init:: dotfiles:: create) ) ]
17
+ CreatingDir ( PathBuf , #[ source] std:: io:: Error ) ,
18
+
19
+ #[ error( "Could not clone git repository \" {0}\" " ) ]
20
+ #[ diagnostic( code( init:: git:: clone) ) ]
21
+ GitClone ( PathBuf , #[ source] git2:: Error ) ,
20
22
}
21
23
22
24
#[ derive( Debug ) ]
@@ -39,6 +41,8 @@ impl Command for Clone {
39
41
fn execute ( & self , ( cli, repo) : Self :: Args ) -> Self :: Result {
40
42
if !cli. dry_run {
41
43
config:: create_config_file ( cli. dotfiles . as_ref ( ) . map ( |d| d. 0 . as_path ( ) ) , & cli. config . 0 ) ?;
44
+
45
+ std:: fs:: create_dir_all ( & self . config . dotfiles ) . map_err ( |err| Error :: CreatingDir ( self . config . dotfiles . clone ( ) , err) ) ?;
42
46
}
43
47
44
48
println ! (
@@ -49,10 +53,68 @@ impl Command for Clone {
49
53
Attribute :: Reset
50
54
) ;
51
55
52
- helpers:: run_command ( "git" , & [ OsStr :: new ( "clone" ) , OsStr :: new ( & repo) , self . config . dotfiles . as_os_str ( ) ] , false , cli. dry_run ) ?;
53
-
54
- println ! ( "\n {}Cloned repo{}" , Attribute :: Bold , Attribute :: Reset ) ;
56
+ if !cli. dry_run {
57
+ clone_git_repo ( & repo, & self . config . dotfiles ) . map_err ( |err| Error :: GitClone ( self . config . dotfiles . clone ( ) , err) ) ?;
58
+ println ! ( "\n {}Cloned repo{}" , Attribute :: Bold , Attribute :: Reset ) ;
59
+ }
55
60
56
61
( ) . pipe ( Ok )
57
62
}
58
63
}
64
+
65
+ fn clone_git_repo ( repo : & str , dotfiles : & std:: path:: Path ) -> Result < Repository , git2:: Error > {
66
+ let mut callbacks = RemoteCallbacks :: new ( ) ;
67
+ callbacks. credentials ( |_url, username_from_url, _allowed_types| Cred :: ssh_key_from_agent ( username_from_url. unwrap ( ) ) ) ;
68
+ let mut fetch_options = git2:: FetchOptions :: new ( ) ;
69
+
70
+ fetch_options. remote_callbacks ( callbacks) ;
71
+
72
+ let mut builder = git2:: build:: RepoBuilder :: new ( ) ;
73
+ builder. fetch_options ( fetch_options) ;
74
+
75
+ builder. clone ( repo, dotfiles) ?. pipe ( Ok )
76
+ }
77
+
78
+ #[ cfg( test) ]
79
+ mod test {
80
+ use std:: path:: Path ;
81
+
82
+ use clap:: Parser ;
83
+ use speculoos:: assert_that;
84
+
85
+ use crate :: { cli, commands:: Command } ;
86
+
87
+ #[ test]
88
+ fn should_clone_repo ( ) {
89
+ let config = crate :: config:: Config {
90
+ dotfiles : Path :: new ( "./target/tmp" ) . to_path_buf ( ) ,
91
+ ..Default :: default ( )
92
+ } ;
93
+
94
+ let config_file = config. dotfiles . parent ( ) . unwrap ( ) . join ( "config.toml" ) ;
95
+ if config_file. exists ( ) {
96
+ std:: fs:: remove_file ( & config_file) . unwrap ( ) ;
97
+ }
98
+ if config. dotfiles . exists ( ) {
99
+ std:: fs:: remove_dir_all ( & config. dotfiles ) . unwrap ( ) ;
100
+ }
101
+ let cli = crate :: cli:: Cli :: parse_from ( [
102
+ "" ,
103
+ "--dotfiles" ,
104
+ & config. dotfiles . to_string_lossy ( ) ,
105
+ "--config" ,
106
+ & config_file. to_string_lossy ( ) ,
107
+ "clone" ,
108
+ "git@github.com:volllly/rotz.git" ,
109
+ ] ) ;
110
+
111
+ let clone = super :: Clone :: new ( config. clone ( ) ) ;
112
+
113
+ let cli:: Command :: Clone { repo } = cli. command . clone ( ) else {
114
+ panic ! ( ) ;
115
+ } ;
116
+
117
+ clone. execute ( ( cli, repo) ) . unwrap ( ) ;
118
+ assert_that ! ( Path :: new( & config. dotfiles) . join( ".git" ) ) . matches ( |p| p. exists ( ) && p. is_dir ( ) ) ;
119
+ }
120
+ }
0 commit comments