1- extern crate regex;
21extern crate linked_hash_map;
3- use self :: regex:: Regex ;
4- use self :: linked_hash_map:: * ;
5- use std:: process:: * ;
2+ extern crate regex;
3+
64use cli:: Config ;
75use file_finder:: CTFile ;
86use log:: debug_log;
7+ use self :: linked_hash_map:: * ;
8+ use self :: regex:: Regex ;
9+ use std:: process:: * ;
910
1011#[ derive( Debug ) ]
1112pub struct RunCommand {
@@ -15,21 +16,31 @@ pub struct RunCommand {
1516}
1617
1718impl RunCommand {
18- pub fn all < ' a > ( file_content : & ' a str , config : Option < & Config > ) -> LinkedHashMap < String , RunCommand > {
19- let regex = Regex :: new ( r"(?m)^\s*([^=]*)=([^#\n]*)(#\s*(.*)\s*)?$" ) . unwrap ( ) ;
19+ pub fn all < ' a > ( file_content : & ' a str , config : & Option < Config > ) -> LinkedHashMap < String , RunCommand > {
20+ let regex = Regex :: new ( r# "(?m)^\s*([^# =]*)=([^#\n]*)(#\s*(.*)\s*)?$"# ) . unwrap ( ) ;
2021 let mut commands: LinkedHashMap < String , RunCommand > = LinkedHashMap :: new ( ) ;
2122 for capture in regex. captures_iter ( file_content) {
2223 let alias = & capture[ 1 ] ;
23- let command_with_args = & capture[ 2 ] . replace ( "\" " , "" ) . replace ( "'" , "" ) ;
24+ debug_log ( || format ! ( " Handling alias : {}" , alias) ) ;
25+ debug_log ( || format ! ( " Raw captured command : {}" , & capture[ 2 ] ) ) ;
26+ let extracted_command = & capture[ 2 ] . trim ( ) ;
27+ let chars = extracted_command. chars ( ) . collect :: < Vec < char > > ( ) ;
28+ let len = chars. len ( ) ;
29+ let command_with_args: & str ;
30+ if ( chars[ 0 ] == '\'' || chars[ 0 ] == '"' ) && chars[ len - 1 ] == chars[ 0 ] {
31+ command_with_args = extracted_command. trim_matches ( chars[ 0 ] ) ;
32+ } else {
33+ command_with_args = extracted_command;
34+ }
35+ debug_log ( || format ! ( " Cleaned command {}" , command_with_args) ) ;
2436
2537 let doc = capture. get ( 4 ) . map ( |m| m. as_str ( ) ) . map ( ToString :: to_string) . unwrap_or ( String :: from ( "" ) ) ;
38+ //this is probably useless since we're running it with sh -c (and probably invalid as first split might not match command if var are exported at beginning of line
2639 let commands_vec: Vec < _ > = command_with_args. split ( " " ) . collect ( ) ;
2740 let ( command, args) = commands_vec. split_first ( ) . unwrap ( ) ;
2841
2942 let mut args_as_vect: Vec < String > = args. iter ( ) . map ( |s| s. to_string ( ) ) . collect ( ) ;
30- if config. is_some ( ) {
31- args_as_vect. append ( & mut config. unwrap ( ) . args . clone ( ) ) ;
32- }
43+ args_as_vect. append ( config. as_ref ( ) . map ( |c| c. args . clone ( ) ) . unwrap_or ( vec ! [ ] ) . as_mut ( ) ) ;
3344 args_as_vect = args_as_vect. into_iter ( ) . filter ( |a| { a. len ( ) > 0 } ) . collect ( ) ;
3445
3546 commands. insert ( alias. to_string ( ) , RunCommand { command : command. to_string ( ) , args : args_as_vect, doc} ) ; ;
@@ -38,19 +49,25 @@ impl RunCommand{
3849 }
3950
4051 pub fn run ( & self , ct_file : & CTFile ) {
41- let mut sh_sub_command = Vec :: new ( ) ;
42- sh_sub_command. push ( self . command . to_string ( ) ) ;
43- sh_sub_command. push ( String :: from ( " " ) ) ;
44- sh_sub_command. push ( self . args . join ( " " ) ) ; // no need to escape "', it is properly handled
45- debug_log ( || format ! ( "About to run `sh -c {:?}`" , sh_sub_command. join( "" ) ) ) ;
52+ let sh_sub_command = self . build_subcommand ( ) ;
53+ debug_log ( || format ! ( "About to run `sh -c {:?}`" , sh_sub_command) ) ;
4654 let s = Command :: new ( "sh" )
55+ . env ( "CTNOBANNER" , "true" )
4756 . arg ( "-c" )
48- . arg ( sh_sub_command. join ( "" ) )
57+ . arg ( sh_sub_command)
4958 . current_dir ( ct_file. path . clone ( ) )
5059 . spawn ( ) . unwrap ( ) ;
5160 //result printed to stdout / stderr as expected as io are shared
5261 let _output = s. wait_with_output ( ) ;
5362 }
63+
64+ fn build_subcommand ( & self ) -> String {
65+ let mut sh_sub_command = Vec :: new ( ) ;
66+ sh_sub_command. push ( self . command . to_string ( ) ) ;
67+ sh_sub_command. push ( String :: from ( " " ) ) ;
68+ sh_sub_command. push ( self . args . join ( " " ) ) ; // no need to escape "', it is properly handled
69+ sh_sub_command. join ( "" )
70+ }
5471}
5572
5673#[ cfg( test) ]
@@ -60,7 +77,7 @@ mod tests{
6077 #[ test]
6178 fn it_should_extract_single_quoted_command ( ) {
6279 let config = Config :: new ( vec ! [ "ct" , "command" ] . into_iter ( ) . map ( ToString :: to_string) . collect ( ) ) . unwrap ( ) ;
63- let map = RunCommand :: all ( "command='run'" , Some ( & config) ) ;
80+ let map = RunCommand :: all ( "command='run'" , & Some ( config) ) ;
6481 let run_command = map. get ( "command" ) . unwrap ( ) ;
6582 assert_eq ! ( run_command. command, "run" ) ;
6683 assert_eq ! ( run_command. args. join( " " ) , "" ) ;
@@ -69,7 +86,7 @@ mod tests{
6986 #[ test]
7087 fn it_should_extract_double_quoted_command ( ) {
7188 let config = Config :: new ( vec ! [ "ct" , "command" ] . into_iter ( ) . map ( ToString :: to_string) . collect ( ) ) . unwrap ( ) ;
72- let map = RunCommand :: all ( "command=\" run\" " , Some ( & config) ) ;
89+ let map = RunCommand :: all ( "command=\" run\" " , & Some ( config) ) ;
7390 let run_command = map. get ( "command" ) . unwrap ( ) ;
7491 assert_eq ! ( run_command. command, "run" ) ;
7592 assert_eq ! ( run_command. args. join( " " ) , "" ) ;
@@ -78,7 +95,7 @@ mod tests{
7895 #[ test]
7996 fn it_should_extract_not_quoted_command ( ) {
8097 let config = Config :: new ( vec ! [ "ct" , "command" ] . into_iter ( ) . map ( ToString :: to_string) . collect ( ) ) . unwrap ( ) ;
81- let map = RunCommand :: all ( "command=run" , Some ( & config) ) ;
98+ let map = RunCommand :: all ( "command=run" , & Some ( config) ) ;
8299 let run_command = map. get ( "command" ) . unwrap ( ) ;
83100 assert_eq ! ( run_command. command, "run" ) ;
84101 assert_eq ! ( run_command. args. join( " " ) , "" ) ;
@@ -87,7 +104,7 @@ mod tests{
87104 #[ test]
88105 fn it_should_append_args_to_run_command_if_no_args_in_run_command ( ) {
89106 let config = Config :: new ( vec ! [ "ct" , "command" , "arg1" , "arg2" ] . into_iter ( ) . map ( ToString :: to_string) . collect ( ) ) . unwrap ( ) ;
90- let map = RunCommand :: all ( "command=run" , Some ( & config) ) ;
107+ let map = RunCommand :: all ( "command=run" , & Some ( config) ) ;
91108 let run_command = map. get ( "command" ) . unwrap ( ) ;
92109 assert_eq ! ( run_command. command, "run" ) ;
93110 assert_eq ! ( run_command. args. join( " " ) , "arg1 arg2" ) ;
@@ -96,7 +113,7 @@ mod tests{
96113 #[ test]
97114 fn it_should_append_args_to_run_command_if_args_in_run_command ( ) {
98115 let config = Config :: new ( vec ! [ "ct" , "command" , "arg1" , "arg2" ] . into_iter ( ) . map ( ToString :: to_string) . collect ( ) ) . unwrap ( ) ;
99- let map = RunCommand :: all ( "command=run tests" , Some ( & config) ) ;
116+ let map = RunCommand :: all ( "command=run tests" , & Some ( config) ) ;
100117 let run_command = map. get ( "command" ) . unwrap ( ) ;
101118 assert_eq ! ( run_command. command, "run" ) ;
102119 assert_eq ! ( run_command. args. join( " " ) , "tests arg1 arg2" ) ;
@@ -107,7 +124,7 @@ mod tests{
107124 let map = RunCommand :: all ( r"command=run tests
108125 command2=run app
109126 command3=push commits
110- " , None ) ;
127+ " , & None ) ;
111128 assert_eq ! ( map. len( ) , 3 ) ;
112129 assert_eq ! ( map. contains_key( "command" ) , true ) ;
113130 assert_eq ! ( map. contains_key( "command2" ) , true ) ;
@@ -116,14 +133,14 @@ mod tests{
116133
117134 #[ test]
118135 fn it_should_match_command_with_leading_spaces ( ) {
119- let map = RunCommand :: all ( " command=run tests" , None ) ;
136+ let map = RunCommand :: all ( " command=run tests" , & None ) ;
120137 assert_eq ! ( map. len( ) , 1 ) ;
121138 assert_eq ! ( map. contains_key( "command" ) , true ) ;
122139 }
123140
124141 #[ test]
125142 fn it_should_match_command_with_doc ( ) {
126- let map = RunCommand :: all ( "command=run tests # this run tests" , None ) ;
143+ let map = RunCommand :: all ( "command=run tests # this run tests" , & None ) ;
127144 assert_eq ! ( map. len( ) , 1 ) ;
128145 let run_command = map. get ( "command" ) . unwrap ( ) ;
129146 assert_eq ! ( run_command. command, "run" ) ;
@@ -134,17 +151,33 @@ mod tests{
134151
135152 #[ test]
136153 fn it_should_match_command_with_leading_tab ( ) {
137- let map = RunCommand :: all ( "\t command=run tests" , None ) ;
154+ let map = RunCommand :: all ( "\t command=run tests" , & None ) ;
155+ assert_eq ! ( map. len( ) , 1 ) ;
156+ assert_eq ! ( map. contains_key( "command" ) , true ) ;
157+ }
158+
159+ #[ test]
160+ fn it_should_remove_surrounding_single_quotes ( ) {
161+ let map = RunCommand :: all ( "command='run tests'" , & None ) ;
162+ assert_eq ! ( map. len( ) , 1 ) ;
163+ assert_eq ! ( map. contains_key( "command" ) , true ) ;
164+ assert_eq ! ( map. get( "command" ) . unwrap( ) . command, "run" ) ;
165+ }
166+
167+ #[ test]
168+ fn it_should_keep_quotes_on_exports ( ) {
169+ let map = RunCommand :: all ( r#"command='VAR="toto tutu";run tests'"# , & None ) ;
138170 assert_eq ! ( map. len( ) , 1 ) ;
139171 assert_eq ! ( map. contains_key( "command" ) , true ) ;
172+ assert_eq ! ( map. get( "command" ) . unwrap( ) . build_subcommand( ) , r#"VAR="toto tutu";run tests"# ) ;
140173 }
141174
142175
143176 #[ test]
144177 #[ should_panic]
145178 fn it_should_error_if_line_does_not_match_pattern ( ) {
146179 let config = Config :: new ( vec ! [ "ct" , "command" ] . into_iter ( ) . map ( ToString :: to_string) . collect ( ) ) . unwrap ( ) ;
147- let map = RunCommand :: all ( "command" , Some ( & config) ) ;
180+ let map = RunCommand :: all ( "command" , & Some ( config) ) ;
148181 let _run_command = map. get ( "command" ) . unwrap ( ) ;
149182 }
150183}
0 commit comments