1
1
//! Manage and merge the various sources of config: shader crate's `Cargo.toml`(s) and CLI args.
2
- use anyhow:: Context as _;
3
- use clap:: Parser as _;
4
2
5
- /// Config
6
- pub struct Config ;
3
+ use std:: path:: Path ;
7
4
8
- impl Config {
9
- /// Get all the config defaults as JSON.
10
- pub fn defaults_as_json ( ) -> anyhow:: Result < serde_json:: Value > {
11
- let defaults_json = Self :: cli_args_to_json ( vec ! [ String :: new( ) ] ) ?;
12
- Ok ( defaults_json)
13
- }
14
-
15
- /// Convert CLI args to their serde JSON representation.
16
- fn cli_args_to_json ( env_args : Vec < String > ) -> anyhow:: Result < serde_json:: Value > {
17
- Ok ( serde_json:: to_value ( crate :: build:: Build :: parse_from (
18
- env_args,
19
- ) ) ?)
20
- }
21
-
22
- /// Config for the `cargo gpu build` and `cargo gpu install` can be set in the shader crate's
23
- /// `Cargo.toml`, so here we load that config first as the base config, and the CLI arguments can
24
- /// then later override it.
25
- pub fn clap_command_with_cargo_config (
26
- shader_crate_path : & std:: path:: PathBuf ,
27
- mut env_args : Vec < String > ,
28
- ) -> anyhow:: Result < crate :: build:: Build > {
29
- let mut config = crate :: metadata:: Metadata :: as_json ( shader_crate_path) ?;
30
-
31
- env_args. retain ( |arg| !( arg == "build" || arg == "install" ) ) ;
32
- let cli_args_json = Self :: cli_args_to_json ( env_args) ?;
33
- Self :: json_merge ( & mut config, cli_args_json, None ) ?;
34
-
35
- let args = serde_json:: from_value :: < crate :: build:: Build > ( config) ?;
36
- Ok ( args)
37
- }
5
+ use serde:: { de:: DeserializeOwned , Serialize } ;
6
+ use serde_json:: { from_value, to_value} ;
38
7
39
- /// Merge 2 JSON objects. But only if the incoming patch value isn't the default value.
40
- /// Inspired by: <https://stackoverflow.com/a/47142105/575773>
41
- pub fn json_merge (
42
- left_in : & mut serde_json:: Value ,
43
- right_in : serde_json:: Value ,
44
- maybe_pointer : Option < & String > ,
45
- ) -> anyhow:: Result < ( ) > {
46
- let defaults = Self :: defaults_as_json ( ) ?;
47
-
48
- match ( left_in, right_in) {
49
- ( left @ & mut serde_json:: Value :: Object ( _) , serde_json:: Value :: Object ( right) ) => {
50
- let left_as_object = left
51
- . as_object_mut ( )
52
- . context ( "Unreachable, we've already proved it's an object" ) ?;
53
- for ( key, value) in right {
54
- let new_pointer = maybe_pointer. as_ref ( ) . map_or_else (
55
- || format ! ( "/{}" , key. clone( ) ) ,
56
- |pointer| format ! ( "{}/{}" , pointer, key. clone( ) ) ,
57
- ) ;
58
- Self :: json_merge (
59
- left_as_object
60
- . entry ( key. clone ( ) )
61
- . or_insert ( serde_json:: Value :: Null ) ,
62
- value,
63
- Some ( & new_pointer) ,
64
- ) ?;
65
- }
66
- }
67
- ( left, right) => {
68
- if let Some ( pointer) = maybe_pointer {
69
- let default = defaults. pointer ( pointer) . context ( format ! (
70
- "Configuration option with path `{pointer}` was not found in the default configuration, \
71
- which is:\n defaults: {defaults:#?}"
72
- ) ) ?;
73
- if & right != default {
74
- // Only overwrite if the new value differs from the defaults.
75
- * left = right;
76
- }
77
- }
78
- }
79
- }
8
+ use crate :: { merge:: json_merge, metadata:: Metadata } ;
80
9
81
- Ok ( ( ) )
82
- }
10
+ /// Config for the subcommand can be set in the shader crate's `Cargo.toml`,
11
+ /// so here we load that config first as the base config, and the CLI arguments can then later override it.
12
+ pub fn clap_command_with_cargo_config < C > ( shader_crate : & Path , config : & C ) -> anyhow:: Result < C >
13
+ where
14
+ C : Default + Serialize + DeserializeOwned ,
15
+ {
16
+ let from_cargo_toml = Metadata :: as_json ( shader_crate) ?;
17
+ let from_cli = to_value ( config) ?;
18
+ let default = to_value ( C :: default ( ) ) ?;
19
+ let final_config = json_merge ( from_cargo_toml, from_cli, & default) ;
20
+ Ok ( from_value ( final_config) ?)
83
21
}
84
22
85
23
#[ cfg( test) ]
86
24
mod test {
87
- use super :: * ;
25
+ use std :: { io :: Write as _ , path :: PathBuf } ;
88
26
89
- use std:: io:: Write as _;
27
+ use clap:: Parser as _;
28
+ use spirv_builder:: Capability ;
29
+
30
+ use crate :: {
31
+ build:: Build ,
32
+ test:: { overwrite_shader_cargo_toml, shader_crate_test_path} ,
33
+ } ;
34
+
35
+ use super :: * ;
90
36
91
37
#[ test_log:: test]
92
38
fn booleans_from_cli ( ) {
93
- let shader_crate_path = crate :: test:: shader_crate_test_path ( ) ;
94
-
95
- let args = Config :: clap_command_with_cargo_config (
96
- & shader_crate_path,
97
- vec ! [
98
- "gpu" . to_owned( ) ,
99
- "build" . to_owned( ) ,
100
- "--debug" . to_owned( ) ,
101
- "--auto-install-rust-toolchain" . to_owned( ) ,
102
- ] ,
103
- )
104
- . unwrap ( ) ;
39
+ let shader_crate_path = shader_crate_test_path ( ) ;
40
+ let config = Build :: parse_from ( [
41
+ "gpu" . as_ref ( ) ,
42
+ // "build".as_ref(),
43
+ "--shader-crate" . as_ref ( ) ,
44
+ shader_crate_path. as_os_str ( ) ,
45
+ "--debug" . as_ref ( ) ,
46
+ "--auto-install-rust-toolchain" . as_ref ( ) ,
47
+ ] ) ;
48
+
49
+ let args = clap_command_with_cargo_config ( & shader_crate_path, & config) . unwrap ( ) ;
105
50
assert ! ( !args. build. spirv_builder. release) ;
106
51
assert ! ( args. install. auto_install_rust_toolchain) ;
107
52
}
108
53
109
54
#[ test_log:: test]
110
55
fn booleans_from_cargo ( ) {
111
- let shader_crate_path = crate :: test:: shader_crate_test_path ( ) ;
112
- let mut file = crate :: test:: overwrite_shader_cargo_toml ( & shader_crate_path) ;
56
+ let shader_crate_path = shader_crate_test_path ( ) ;
57
+
58
+ let mut file = overwrite_shader_cargo_toml ( & shader_crate_path) ;
113
59
file. write_all (
114
60
[
115
61
"[package.metadata.rust-gpu.build]" ,
@@ -122,14 +68,21 @@ mod test {
122
68
)
123
69
. unwrap ( ) ;
124
70
125
- let args = Config :: clap_command_with_cargo_config ( & shader_crate_path, vec ! [ ] ) . unwrap ( ) ;
71
+ let config = Build :: parse_from ( [
72
+ "gpu" . as_ref ( ) ,
73
+ // "build".as_ref(),
74
+ "--shader-crate" . as_ref ( ) ,
75
+ shader_crate_path. as_os_str ( ) ,
76
+ ] ) ;
77
+
78
+ let args = clap_command_with_cargo_config ( & shader_crate_path, & config) . unwrap ( ) ;
126
79
assert ! ( !args. build. spirv_builder. release) ;
127
80
assert ! ( args. install. auto_install_rust_toolchain) ;
128
81
}
129
82
130
- fn update_cargo_output_dir ( ) -> std :: path :: PathBuf {
131
- let shader_crate_path = crate :: test :: shader_crate_test_path ( ) ;
132
- let mut file = crate :: test :: overwrite_shader_cargo_toml ( & shader_crate_path) ;
83
+ fn update_cargo_output_dir ( ) -> PathBuf {
84
+ let shader_crate_path = shader_crate_test_path ( ) ;
85
+ let mut file = overwrite_shader_cargo_toml ( & shader_crate_path) ;
133
86
file. write_all (
134
87
[
135
88
"[package.metadata.rust-gpu.build]" ,
@@ -145,35 +98,41 @@ mod test {
145
98
#[ test_log:: test]
146
99
fn string_from_cargo ( ) {
147
100
let shader_crate_path = update_cargo_output_dir ( ) ;
148
-
149
- let args = Config :: clap_command_with_cargo_config ( & shader_crate_path, vec ! [ ] ) . unwrap ( ) ;
101
+ let config = Build :: parse_from ( [
102
+ "gpu" . as_ref ( ) ,
103
+ // "build".as_ref(),
104
+ "--shader-crate" . as_ref ( ) ,
105
+ shader_crate_path. as_os_str ( ) ,
106
+ ] ) ;
107
+
108
+ let args = clap_command_with_cargo_config ( & shader_crate_path, & config) . unwrap ( ) ;
150
109
if cfg ! ( target_os = "windows" ) {
151
- assert_eq ! ( args. build. output_dir, std :: path :: Path :: new( "C:/the/moon" ) ) ;
110
+ assert_eq ! ( args. build. output_dir, Path :: new( "C:/the/moon" ) ) ;
152
111
} else {
153
- assert_eq ! ( args. build. output_dir, std :: path :: Path :: new( "/the/moon" ) ) ;
112
+ assert_eq ! ( args. build. output_dir, Path :: new( "/the/moon" ) ) ;
154
113
}
155
114
}
156
115
157
116
#[ test_log:: test]
158
117
fn string_from_cargo_overwritten_by_cli ( ) {
159
118
let shader_crate_path = update_cargo_output_dir ( ) ;
160
-
161
- let args = Config :: clap_command_with_cargo_config (
162
- & shader_crate_path,
163
- vec ! [
164
- "gpu" . to_owned( ) ,
165
- "build" . to_owned( ) ,
166
- "--output-dir" . to_owned( ) ,
167
- "/the/river" . to_owned( ) ,
168
- ] ,
169
- )
170
- . unwrap ( ) ;
171
- assert_eq ! ( args. build. output_dir, std:: path:: Path :: new( "/the/river" ) ) ;
119
+ let config = Build :: parse_from ( [
120
+ "gpu" . as_ref ( ) ,
121
+ // "build".as_ref(),
122
+ "--shader-crate" . as_ref ( ) ,
123
+ shader_crate_path. as_os_str ( ) ,
124
+ "--output-dir" . as_ref ( ) ,
125
+ "/the/river" . as_ref ( ) ,
126
+ ] ) ;
127
+
128
+ let args = clap_command_with_cargo_config ( & shader_crate_path, & config) . unwrap ( ) ;
129
+ assert_eq ! ( args. build. output_dir, Path :: new( "/the/river" ) ) ;
172
130
}
173
131
174
132
#[ test_log:: test]
175
133
fn arrays_from_cargo ( ) {
176
134
let shader_crate_path = crate :: test:: shader_crate_test_path ( ) ;
135
+
177
136
let mut file = crate :: test:: overwrite_shader_cargo_toml ( & shader_crate_path) ;
178
137
file. write_all (
179
138
[
@@ -185,30 +144,33 @@ mod test {
185
144
)
186
145
. unwrap ( ) ;
187
146
188
- let args = Config :: clap_command_with_cargo_config ( & shader_crate_path, vec ! [ ] ) . unwrap ( ) ;
147
+ let config = Build :: parse_from ( [
148
+ "gpu" . as_ref ( ) ,
149
+ // "build".as_ref(),
150
+ "--shader-crate" . as_ref ( ) ,
151
+ shader_crate_path. as_os_str ( ) ,
152
+ ] ) ;
153
+
154
+ let args = clap_command_with_cargo_config ( & shader_crate_path, & config) . unwrap ( ) ;
189
155
assert_eq ! (
190
156
args. build. spirv_builder. capabilities,
191
- vec![
192
- spirv_builder:: Capability :: AtomicStorage ,
193
- spirv_builder:: Capability :: Matrix
194
- ]
157
+ [ Capability :: AtomicStorage , Capability :: Matrix ]
195
158
) ;
196
159
}
197
160
198
161
#[ test_log:: test]
199
162
fn rename_manifest_parse ( ) {
200
163
let shader_crate_path = crate :: test:: shader_crate_test_path ( ) ;
201
-
202
- let args = Config :: clap_command_with_cargo_config (
203
- & shader_crate_path,
204
- vec ! [
205
- "gpu" . to_owned( ) ,
206
- "build" . to_owned( ) ,
207
- "--manifest-file" . to_owned( ) ,
208
- "mymanifest" . to_owned( ) ,
209
- ] ,
210
- )
211
- . unwrap ( ) ;
164
+ let config = Build :: parse_from ( [
165
+ "gpu" . as_ref ( ) ,
166
+ // "build".as_ref(),
167
+ "--shader-crate" . as_ref ( ) ,
168
+ shader_crate_path. as_os_str ( ) ,
169
+ "--manifest-file" . as_ref ( ) ,
170
+ "mymanifest" . as_ref ( ) ,
171
+ ] ) ;
172
+
173
+ let args = clap_command_with_cargo_config ( & shader_crate_path, & config) . unwrap ( ) ;
212
174
assert_eq ! ( args. build. manifest_file, "mymanifest" . to_owned( ) ) ;
213
175
}
214
176
}
0 commit comments