@@ -70,7 +70,7 @@ impl Default for AppConfig {
7070 write_mode : "append" . to_string ( ) ,
7171 history_limit : 50 ,
7272 locale : None ,
73- theme : "light " . to_string ( ) ,
73+ theme : "system " . to_string ( ) ,
7474 choice_mode : 2 ,
7575 show_title_on_tray : false ,
7676 hide_at_launch : false ,
@@ -95,6 +95,12 @@ impl Default for AppConfig {
9595}
9696
9797impl AppConfig {
98+ fn normalize ( & mut self ) {
99+ if !matches ! ( self . theme. as_str( ) , "light" | "dark" | "system" ) {
100+ self . theme = "system" . to_string ( ) ;
101+ }
102+ }
103+
98104 /// Load `internal/config.json`.
99105 ///
100106 /// - Missing file → default config in memory, no write.
@@ -107,7 +113,10 @@ impl AppConfig {
107113 }
108114 match std:: fs:: read ( path) {
109115 Ok ( bytes) => match serde_json:: from_slice :: < AppConfig > ( & bytes) {
110- Ok ( cfg) => cfg,
116+ Ok ( mut cfg) => {
117+ cfg. normalize ( ) ;
118+ cfg
119+ }
111120 Err ( e) => {
112121 log:: warn!(
113122 "config.json at {} failed to parse: {e}. Falling back to defaults in memory; the file will not be overwritten until the next explicit write." ,
@@ -180,12 +189,13 @@ impl AppConfig {
180189 merged_obj. insert ( k. clone ( ) , v. clone ( ) ) ;
181190 }
182191
183- let next: AppConfig = serde_json:: from_value ( merged) . map_err ( |e| {
192+ let mut next: AppConfig = serde_json:: from_value ( merged) . map_err ( |e| {
184193 StorageError :: InvalidConfigValue {
185194 key : "<patch>" . into ( ) ,
186195 reason : e. to_string ( ) ,
187196 }
188197 } ) ?;
198+ next. normalize ( ) ;
189199 * self = next;
190200 Ok ( ( ) )
191201 }
@@ -198,3 +208,34 @@ impl AppConfig {
198208 }
199209}
200210
211+ #[ cfg( test) ]
212+ mod tests {
213+ use super :: * ;
214+
215+ #[ test]
216+ fn load_normalizes_invalid_theme_to_system ( ) {
217+ let path = std:: env:: temp_dir ( ) . join ( format ! (
218+ "switchhosts-config-test-{}-{}.json" ,
219+ std:: process:: id( ) ,
220+ std:: time:: SystemTime :: now( )
221+ . duration_since( std:: time:: UNIX_EPOCH )
222+ . unwrap( )
223+ . as_nanos( )
224+ ) ) ;
225+ std:: fs:: write ( & path, r#"{ "theme": "sepia" }"# ) . unwrap ( ) ;
226+
227+ let cfg = AppConfig :: load ( & path) ;
228+ let _ = std:: fs:: remove_file ( path) ;
229+
230+ assert_eq ! ( cfg. theme, "system" ) ;
231+ }
232+
233+ #[ test]
234+ fn apply_partial_normalizes_invalid_theme_to_system ( ) {
235+ let mut cfg = AppConfig :: default ( ) ;
236+
237+ cfg. apply_partial ( & json ! ( { "theme" : "sepia" } ) ) . unwrap ( ) ;
238+
239+ assert_eq ! ( cfg. theme, "system" ) ;
240+ }
241+ }
0 commit comments