1
- use std:: collections:: BTreeMap ;
2
- use std:: ffi:: OsString ;
3
- use std:: fmt:: Write ;
4
-
5
1
use anyhow:: { anyhow, Result } ;
6
2
use indoc:: formatdoc;
7
- use serde:: { Serialize , Serializer } ;
8
- use smol_str:: SmolStr ;
9
-
3
+ use itertools:: Itertools ;
10
4
use scarb:: core:: errors:: ScriptExecutionError ;
11
- use scarb:: core:: { Config , Package , Workspace } ;
5
+ use scarb:: core:: { Config , Package , PackageName , ScriptDefinition , Workspace } ;
12
6
use scarb:: ops;
13
7
use scarb_ui:: Message ;
8
+ use serde:: { Serialize , Serializer } ;
9
+ use smol_str:: SmolStr ;
10
+ use std:: collections:: BTreeMap ;
11
+ use std:: ffi:: OsString ;
12
+ use std:: fmt:: Write ;
14
13
15
14
use crate :: args:: ScriptsRunnerArgs ;
16
15
use crate :: errors:: ErrorWithExitCode ;
17
16
18
17
#[ tracing:: instrument( skip_all, level = "info" ) ]
19
18
pub fn run ( args : ScriptsRunnerArgs , config : & Config ) -> Result < ( ) > {
20
19
let ws = ops:: read_workspace ( config. manifest_path ( ) , config) ?;
21
- let packages = args. packages_filter . match_many ( & ws) ?;
22
- let errors = packages
20
+ let errors = if args. workspace_root {
21
+ run_for_workspace_root ( args, & ws)
22
+ . err ( )
23
+ . into_iter ( )
24
+ . collect_vec ( )
25
+ } else {
26
+ run_for_packages ( args, & ws) ?
27
+ . into_iter ( )
28
+ . filter_map ( |res| res. err ( ) )
29
+ . map ( |res| anyhow ! ( res) )
30
+ . collect :: < Vec < anyhow:: Error > > ( )
31
+ } ;
32
+ build_exit_error ( errors)
33
+ }
34
+
35
+ fn run_for_workspace_root ( args : ScriptsRunnerArgs , ws : & Workspace ) -> Result < ( ) > {
36
+ args. script
37
+ . map ( |script| {
38
+ let script_definition = ws. script ( & script) . ok_or_else ( || {
39
+ missing_script_error ( & script, "workspace root" , " --workspace-root" )
40
+ } ) ?;
41
+ ops:: execute_script ( script_definition, & args. args , ws, ws. root ( ) , None )
42
+ } )
43
+ . unwrap_or_else ( || {
44
+ ws. config ( )
45
+ . ui ( )
46
+ . print ( ScriptsList :: for_workspace_root ( ws. scripts ( ) . clone ( ) ) ) ;
47
+ Ok ( ( ) )
48
+ } )
49
+ }
50
+
51
+ fn run_for_packages ( args : ScriptsRunnerArgs , ws : & Workspace ) -> Result < Vec < Result < ( ) > > > {
52
+ Ok ( args
53
+ . packages_filter
54
+ . match_many ( ws) ?
23
55
. into_iter ( )
24
56
. map ( |package| {
25
57
args. script
26
58
. clone ( )
27
- . map ( |script| run_script ( script, & args. args , package. clone ( ) , & ws) )
28
- . unwrap_or_else ( || list_scripts ( package, & ws) )
59
+ . map ( |script| run_package_script ( script, & args. args , package. clone ( ) , ws) )
60
+ . unwrap_or_else ( || {
61
+ ws. config ( ) . ui ( ) . print ( ScriptsList :: for_package (
62
+ package. id . name . clone ( ) ,
63
+ package. manifest . scripts . clone ( ) ,
64
+ ws. is_single_package ( ) ,
65
+ ) ) ;
66
+ Ok ( ( ) )
67
+ } )
29
68
} )
30
- . filter_map ( |res| res. err ( ) )
31
- . map ( |res| anyhow ! ( res) )
32
- . collect :: < Vec < anyhow:: Error > > ( ) ;
69
+ . collect_vec ( ) )
70
+ }
71
+
72
+ fn build_exit_error ( errors : Vec < anyhow:: Error > ) -> Result < ( ) > {
33
73
if errors. is_empty ( ) {
34
74
Ok ( ( ) )
35
75
} else {
@@ -50,63 +90,132 @@ pub fn run(args: ScriptsRunnerArgs, config: &Config) -> Result<()> {
50
90
}
51
91
}
52
92
53
- fn run_script ( script : SmolStr , args : & [ OsString ] , package : Package , ws : & Workspace ) -> Result < ( ) > {
93
+ fn run_package_script (
94
+ script : SmolStr ,
95
+ args : & [ OsString ] ,
96
+ package : Package ,
97
+ ws : & Workspace ,
98
+ ) -> Result < ( ) > {
54
99
let script_definition = package. manifest . scripts . get ( & script) . ok_or_else ( || {
55
100
let package_name = package. id . name . to_string ( ) ;
56
- let package_selector = if !ws. is_single_package ( ) {
57
- format ! ( " -p {package_name}" )
58
- } else {
101
+ let package_selector = if ws. is_single_package ( ) {
59
102
String :: new ( )
103
+ } else {
104
+ format ! ( " -p {package_name}" )
60
105
} ;
61
- anyhow ! ( formatdoc! { r#"
62
- missing script `{script}` for package: {package_name}
63
-
64
- To see a list of scripts, run:
65
- scarb run{package_selector}
66
- "# } )
106
+ missing_script_error (
107
+ & script,
108
+ & format ! ( "package: {package_name}" ) ,
109
+ & package_selector,
110
+ )
67
111
} ) ?;
68
112
ops:: execute_script ( script_definition, args, ws, package. root ( ) , None )
69
113
}
70
114
71
- fn list_scripts ( package : Package , ws : & Workspace ) -> Result < ( ) > {
72
- let scripts = package
73
- . manifest
74
- . scripts
75
- . iter ( )
76
- . map ( |( name, definition) | ( name. to_string ( ) , definition. to_string ( ) ) )
77
- . collect ( ) ;
78
- let package = package. id . name . to_string ( ) ;
79
- let single_package = ws. is_single_package ( ) ;
80
- ws. config ( ) . ui ( ) . print ( ScriptsList {
81
- scripts,
82
- package,
83
- single_package,
84
- } ) ;
85
- Ok ( ( ) )
115
+ fn missing_script_error ( script : & str , source : & str , selector : & str ) -> anyhow:: Error {
116
+ anyhow ! ( formatdoc! { r#"
117
+ missing script `{script}` for {source}
118
+
119
+ To see a list of scripts, run:
120
+ scarb run{selector}
121
+ "# } )
86
122
}
87
123
88
124
#[ derive( Serialize , Debug ) ]
89
- struct ScriptsList {
125
+ struct PackageScriptsList {
90
126
package : String ,
91
127
scripts : BTreeMap < String , String > ,
92
128
single_package : bool ,
93
129
}
94
130
95
- impl Message for ScriptsList {
96
- fn text ( self ) -> String {
131
+ #[ derive( Serialize , Debug ) ]
132
+ struct WorkspaceScriptsList {
133
+ scripts : BTreeMap < String , String > ,
134
+ }
135
+
136
+ #[ derive( Serialize , Debug ) ]
137
+ #[ serde( untagged) ]
138
+ enum ScriptsList {
139
+ ForPackage ( PackageScriptsList ) ,
140
+ ForWorkspaceRoot ( WorkspaceScriptsList ) ,
141
+ }
142
+
143
+ impl ScriptsList {
144
+ pub fn for_package (
145
+ package : PackageName ,
146
+ scripts : BTreeMap < SmolStr , ScriptDefinition > ,
147
+ single_package : bool ,
148
+ ) -> Self {
149
+ let scripts = scripts
150
+ . iter ( )
151
+ . map ( |( name, definition) | ( name. to_string ( ) , definition. to_string ( ) ) )
152
+ . collect ( ) ;
153
+ Self :: ForPackage ( PackageScriptsList {
154
+ package : package. to_string ( ) ,
155
+ scripts,
156
+ single_package,
157
+ } )
158
+ }
159
+
160
+ pub fn for_workspace_root ( scripts : BTreeMap < SmolStr , ScriptDefinition > ) -> Self {
161
+ let scripts = scripts
162
+ . iter ( )
163
+ . map ( |( name, definition) | ( name. to_string ( ) , definition. to_string ( ) ) )
164
+ . collect ( ) ;
165
+ Self :: ForWorkspaceRoot ( WorkspaceScriptsList { scripts } )
166
+ }
167
+
168
+ fn scripts ( & self ) -> & BTreeMap < String , String > {
169
+ match self {
170
+ Self :: ForPackage ( p) => & p. scripts ,
171
+ Self :: ForWorkspaceRoot ( w) => & w. scripts ,
172
+ }
173
+ }
174
+ }
175
+
176
+ impl PackageScriptsList {
177
+ pub fn text ( & self ) -> String {
97
178
let mut text = String :: new ( ) ;
98
179
write ! ( text, "Scripts available via `scarb run`" , ) . unwrap ( ) ;
99
180
if !self . single_package {
100
181
write ! ( text, " for package `{}`" , self . package) . unwrap ( ) ;
101
182
}
102
183
writeln ! ( text, ":" , ) . unwrap ( ) ;
103
- for ( name, definition) in self . scripts {
104
- writeln ! ( text, "{:<22}: {}" , name, definition) . unwrap ( ) ;
105
- }
184
+ write ! ( text, "{}" , write_scripts( & self . scripts) ) . unwrap ( ) ;
185
+ text
186
+ }
187
+ }
188
+
189
+ impl WorkspaceScriptsList {
190
+ pub fn text ( & self ) -> String {
191
+ let mut text = String :: new ( ) ;
192
+ writeln ! (
193
+ text,
194
+ "Scripts available via `scarb run` for workspace root:" ,
195
+ )
196
+ . unwrap ( ) ;
197
+ write ! ( text, "{}" , write_scripts( & self . scripts) ) . unwrap ( ) ;
106
198
text
107
199
}
200
+ }
201
+
202
+ fn write_scripts ( scripts : & BTreeMap < String , String > ) -> String {
203
+ let mut text = String :: new ( ) ;
204
+ for ( name, definition) in scripts {
205
+ writeln ! ( text, "{:<22}: {}" , name, definition) . unwrap ( ) ;
206
+ }
207
+ text
208
+ }
209
+
210
+ impl Message for ScriptsList {
211
+ fn text ( self ) -> String {
212
+ match self {
213
+ Self :: ForPackage ( p) => p. text ( ) ,
214
+ Self :: ForWorkspaceRoot ( w) => w. text ( ) ,
215
+ }
216
+ }
108
217
109
218
fn structured < S : Serializer > ( self , ser : S ) -> Result < S :: Ok , S :: Error > {
110
- self . scripts . serialize ( ser)
219
+ self . scripts ( ) . serialize ( ser)
111
220
}
112
221
}
0 commit comments