1010use std:: {
1111 ffi:: OsStr ,
1212 fs:: { create_dir_all, read_to_string, write} ,
13- path:: PathBuf ,
13+ path:: { Path , PathBuf } ,
1414} ;
1515
1616use directories:: ProjectDirs ;
@@ -91,7 +91,7 @@ pub struct ProjectConfig {
9191 #[ serde( default ) ]
9292 pub format : ImageFormatKind ,
9393 /// ESP-IDF format arguments
94- #[ serde( default ) ]
94+ #[ serde( default , alias = "idf" ) ]
9595 pub idf_format_args : cli:: IdfFormatArgs ,
9696 /// Flash settings
9797 #[ serde( default ) ]
@@ -113,62 +113,128 @@ pub struct PortConfig {
113113}
114114
115115impl Config {
116- /// Load configuration from the configuration files
116+ /// Load configuration from the configuration files.
117117 pub fn load ( ) -> Result < Self > {
118118 let project_config_file = Self :: project_config_path ( ) ?;
119119 let port_config_file = Self :: port_config_path ( ) ?;
120120
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 ( ( ) )
123179 } 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+ }
126188
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 ( ) ) ,
131194 }
195+ } else {
196+ Ok ( ( ) )
132197 }
198+ }
133199
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" ) ) {
136203 return Err ( Error :: InvalidBootloaderPath . into ( ) ) ;
137204 }
138205 }
206+ Ok ( ( ) )
207+ }
139208
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) {
145217 if data. contains ( "[connection]" ) || data. contains ( "[[usb_device]]" ) {
146218 log:: info!(
147219 "espflash@3 configuration detected. Migrating port config to port_config_file: {:#?}" ,
148220 & port_config_file
149221 ) ;
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) ?;
154222
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)
156229 } else {
157- PortConfig :: default ( )
230+ Ok ( PortConfig :: default ( ) )
158231 }
159232 } 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+ }
169235 }
170236
171- fn write_config < T : Serialize > ( config : & T , path : & PathBuf ) -> Result < ( ) > {
237+ fn write_config < T : Serialize > ( config : & T , path : & Path ) -> Result < ( ) > {
172238 let serialized = toml:: to_string ( config)
173239 . into_diagnostic ( )
174240 . wrap_err ( "Failed to serialize config" ) ?;
0 commit comments