10
10
use std:: {
11
11
ffi:: OsStr ,
12
12
fs:: { create_dir_all, read_to_string, write} ,
13
- path:: PathBuf ,
13
+ path:: { Path , PathBuf } ,
14
14
} ;
15
15
16
16
use directories:: ProjectDirs ;
@@ -91,7 +91,7 @@ pub struct ProjectConfig {
91
91
#[ serde( default ) ]
92
92
pub format : ImageFormatKind ,
93
93
/// ESP-IDF format arguments
94
- #[ serde( default ) ]
94
+ #[ serde( default , alias = "idf" ) ]
95
95
pub idf_format_args : cli:: IdfFormatArgs ,
96
96
/// Flash settings
97
97
#[ serde( default ) ]
@@ -113,62 +113,128 @@ pub struct PortConfig {
113
113
}
114
114
115
115
impl Config {
116
- /// Load configuration from the configuration files
116
+ /// Load configuration from the configuration files.
117
117
pub fn load ( ) -> Result < Self > {
118
118
let project_config_file = Self :: project_config_path ( ) ?;
119
119
let port_config_file = Self :: port_config_path ( ) ?;
120
120
121
- let project_config = if let Ok ( data) = read_to_string ( & project_config_file) {
122
- toml:: from_str ( & data) . into_diagnostic ( ) ?
121
+ let raw_data = read_to_string ( & project_config_file) . unwrap_or_default ( ) ;
122
+ let toml_value = toml:: from_str :: < toml:: Value > ( & raw_data)
123
+ . unwrap_or_else ( |_| toml:: Value :: Table ( Default :: default ( ) ) ) ;
124
+
125
+ if let toml:: Value :: Table ( top_level) = & toml_value {
126
+ Self :: validate_keys ( top_level) ?;
127
+ }
128
+
129
+ let project_config: ProjectConfig =
130
+ toml:: from_str ( & raw_data) . unwrap_or_else ( |_| ProjectConfig :: default ( ) ) ;
131
+
132
+ Self :: validate_partition_table_path ( & project_config) ?;
133
+ Self :: validate_bootloader_path ( & project_config) ?;
134
+
135
+ debug ! ( "Config: {:#?}" , & project_config) ;
136
+
137
+ let mut port_config =
138
+ Self :: load_port_config ( & port_config_file, & project_config_file, & raw_data) ?;
139
+ port_config. save_path = port_config_file;
140
+ debug ! ( "Port Config: {:#?}" , & port_config) ;
141
+
142
+ Ok ( Config {
143
+ project_config,
144
+ port_config,
145
+ } )
146
+ }
147
+
148
+ fn validate_keys ( top_level : & toml:: map:: Map < String , toml:: Value > ) -> Result < ( ) > {
149
+ let forbidden_keys: & [ & [ & str ] ] = & [
150
+ & [
151
+ "bootloader" ,
152
+ "partition_table" ,
153
+ "partition_table_offset" ,
154
+ "target_app_partition" ,
155
+ ] ,
156
+ & [ "size" , "mode" , "frequency" ] ,
157
+ ] ;
158
+ let allowed_sections: & [ & [ & str ] ] = & [ & [ "idf_format_args" , "idf" ] , & [ "flash" ] ] ;
159
+
160
+ let mut misplaced_keys = Vec :: new ( ) ;
161
+
162
+ for ( section_keys, allowed) in forbidden_keys. iter ( ) . zip ( allowed_sections. iter ( ) ) {
163
+ for & key in * section_keys {
164
+ for ( section_name, value) in top_level {
165
+ if let toml:: Value :: Table ( table) = value {
166
+ if table. contains_key ( key) && !allowed. contains ( & section_name. as_str ( ) ) {
167
+ misplaced_keys. push ( ( key, allowed[ 0 ] ) ) ;
168
+ }
169
+ }
170
+ }
171
+ if top_level. contains_key ( key) {
172
+ misplaced_keys. push ( ( key, allowed[ 0 ] ) ) ;
173
+ }
174
+ }
175
+ }
176
+
177
+ if misplaced_keys. is_empty ( ) {
178
+ Ok ( ( ) )
123
179
} else {
124
- ProjectConfig :: default ( )
125
- } ;
180
+ let msg = misplaced_keys
181
+ . into_iter ( )
182
+ . map ( |( key, section) | format ! ( "'{key}' should be under [{section}]!" ) )
183
+ . collect :: < Vec < _ > > ( )
184
+ . join ( ", " ) ;
185
+ Err ( Error :: MisplacedKey ( msg) . into ( ) )
186
+ }
187
+ }
126
188
127
- if let Some ( table) = & project_config. idf_format_args . partition_table {
128
- match table. extension ( ) {
129
- Some ( ext) if ext == "bin" || ext == "csv" => { }
130
- _ => return Err ( Error :: InvalidPartitionTablePath . into ( ) ) ,
189
+ fn validate_partition_table_path ( config : & ProjectConfig ) -> Result < ( ) > {
190
+ if let Some ( path) = & config. idf_format_args . partition_table {
191
+ match path. extension ( ) {
192
+ Some ( ext) if ext == "bin" || ext == "csv" => Ok ( ( ) ) ,
193
+ _ => Err ( Error :: InvalidPartitionTablePath . into ( ) ) ,
131
194
}
195
+ } else {
196
+ Ok ( ( ) )
132
197
}
198
+ }
133
199
134
- if let Some ( bootloader) = & project_config. idf_format_args . bootloader {
135
- if bootloader. extension ( ) != Some ( OsStr :: new ( "bin" ) ) {
200
+ fn validate_bootloader_path ( config : & ProjectConfig ) -> Result < ( ) > {
201
+ if let Some ( path) = & config. idf_format_args . bootloader {
202
+ if path. extension ( ) != Some ( OsStr :: new ( "bin" ) ) {
136
203
return Err ( Error :: InvalidBootloaderPath . into ( ) ) ;
137
204
}
138
205
}
206
+ Ok ( ( ) )
207
+ }
139
208
140
- debug ! ( "Config: {:#?}" , & project_config) ;
141
-
142
- let mut port_config = if let Ok ( data) = read_to_string ( & port_config_file) {
143
- toml:: from_str ( & data) . into_diagnostic ( ) ?
144
- } else if let Ok ( data) = read_to_string ( & project_config_file) {
209
+ fn load_port_config (
210
+ port_config_file : & Path ,
211
+ project_config_file : & Path ,
212
+ raw_data : & str ,
213
+ ) -> Result < PortConfig > {
214
+ if let Ok ( data) = read_to_string ( port_config_file) {
215
+ toml:: from_str ( & data) . into_diagnostic ( )
216
+ } else if let Ok ( data) = read_to_string ( project_config_file) {
145
217
if data. contains ( "[connection]" ) || data. contains ( "[[usb_device]]" ) {
146
218
log:: info!(
147
219
"espflash@3 configuration detected. Migrating port config to port_config_file: {:#?}" ,
148
220
& port_config_file
149
221
) ;
150
- // Write the updated configs
151
- let port_config = toml:: from_str ( & data) . into_diagnostic ( ) ?;
152
- Self :: write_config ( & port_config, & port_config_file) ?;
153
- Self :: write_config ( & project_config, & project_config_file) ?;
154
222
155
- port_config
223
+ let port_config: PortConfig = toml:: from_str ( & data) . into_diagnostic ( ) ?;
224
+ let project_config: ProjectConfig = toml:: from_str ( raw_data) . unwrap_or_default ( ) ;
225
+
226
+ Self :: write_config ( & port_config, port_config_file) ?;
227
+ Self :: write_config ( & project_config, project_config_file) ?;
228
+ Ok ( port_config)
156
229
} else {
157
- PortConfig :: default ( )
230
+ Ok ( PortConfig :: default ( ) )
158
231
}
159
232
} else {
160
- PortConfig :: default ( )
161
- } ;
162
- port_config. save_path = port_config_file;
163
- debug ! ( "Port Config: {:#?}" , & port_config) ;
164
-
165
- Ok ( Config {
166
- project_config,
167
- port_config,
168
- } )
233
+ Ok ( PortConfig :: default ( ) )
234
+ }
169
235
}
170
236
171
- fn write_config < T : Serialize > ( config : & T , path : & PathBuf ) -> Result < ( ) > {
237
+ fn write_config < T : Serialize > ( config : & T , path : & Path ) -> Result < ( ) > {
172
238
let serialized = toml:: to_string ( config)
173
239
. into_diagnostic ( )
174
240
. wrap_err ( "Failed to serialize config" ) ?;
0 commit comments