11use serde:: { Deserialize , Serialize } ;
22use std:: collections:: HashMap ;
33use std:: fs;
4- use std:: path:: PathBuf ;
4+ use std:: path:: { Path , PathBuf } ;
55
66#[ cfg( test) ]
77mod tests;
88
9+ #[ derive( Debug , Clone ) ]
10+ pub enum ConfigFormat {
11+ Json ,
12+ Toml ,
13+ }
14+
915#[ derive( Debug , Clone , Serialize , Deserialize ) ]
1016pub struct PrinterConfig {
1117 pub name : String ,
@@ -77,16 +83,57 @@ impl Default for MqttSettings {
7783}
7884
7985impl AppConfig {
86+ pub fn detect_format ( path : & Path ) -> ConfigFormat {
87+ match path. extension ( ) . and_then ( |ext| ext. to_str ( ) ) {
88+ Some ( "toml" ) => ConfigFormat :: Toml ,
89+ Some ( "json" ) => ConfigFormat :: Json ,
90+ _ => ConfigFormat :: Toml , // Default to TOML for new configs
91+ }
92+ }
93+
8094 pub fn load_from_file ( path : & PathBuf ) -> Result < Self , ConfigError > {
95+ // Try both formats if the specified file doesn't exist
8196 if !path. exists ( ) {
97+ // Try to find existing config in either format
98+ if let Some ( existing_path) = Self :: find_existing_config_file ( path) {
99+ return Self :: load_from_existing_file ( & existing_path) ;
100+ }
82101 return Ok ( Self :: default ( ) ) ;
83102 }
84103
104+ Self :: load_from_existing_file ( path)
105+ }
106+
107+ fn find_existing_config_file ( preferred_path : & Path ) -> Option < PathBuf > {
108+ let base_dir = preferred_path. parent ( ) ?;
109+
110+ // Try TOML first, then JSON
111+ let toml_path = base_dir. join ( "config.toml" ) ;
112+ let json_path = base_dir. join ( "config.json" ) ;
113+
114+ if toml_path. exists ( ) {
115+ Some ( toml_path)
116+ } else if json_path. exists ( ) {
117+ Some ( json_path)
118+ } else {
119+ None
120+ }
121+ }
122+
123+ fn load_from_existing_file ( path : & PathBuf ) -> Result < Self , ConfigError > {
85124 let contents = fs:: read_to_string ( path)
86125 . map_err ( |e| ConfigError :: IoError ( format ! ( "Failed to read config file: {e}" ) ) ) ?;
87126
88- let config: AppConfig = serde_json:: from_str ( & contents)
89- . map_err ( |e| ConfigError :: ParseError ( format ! ( "Failed to parse config: {e}" ) ) ) ?;
127+ let format = Self :: detect_format ( path) ;
128+
129+ let config: AppConfig = match format {
130+ ConfigFormat :: Json => serde_json:: from_str ( & contents) . map_err ( |e| {
131+ ConfigError :: ParseError ( format ! ( "Failed to parse JSON config: {e}" ) )
132+ } ) ?,
133+ ConfigFormat :: Toml => toml:: from_str ( & contents) . map_err ( |e| {
134+ ConfigError :: ParseError ( format ! ( "Failed to parse TOML config: {e}" ) )
135+ } ) ?,
136+ } ;
90137
91138 Ok ( config)
92139 }
@@ -98,8 +145,16 @@ impl AppConfig {
98145 } ) ?;
99146 }
100147
101- let contents = serde_json:: to_string_pretty ( self )
102- . map_err ( |e| ConfigError :: SerializeError ( format ! ( "Failed to serialize config: {e}" ) ) ) ?;
148+ let format = Self :: detect_format ( path) ;
149+
150+ let contents = match format {
151+ ConfigFormat :: Json => serde_json:: to_string_pretty ( self ) . map_err ( |e| {
152+ ConfigError :: SerializeError ( format ! ( "Failed to serialize JSON config: {e}" ) )
153+ } ) ?,
154+ ConfigFormat :: Toml => toml:: to_string_pretty ( self ) . map_err ( |e| {
155+ ConfigError :: SerializeError ( format ! ( "Failed to serialize TOML config: {e}" ) )
156+ } ) ?,
157+ } ;
103158
104159 fs:: write ( path, contents)
105160 . map_err ( |e| ConfigError :: IoError ( format ! ( "Failed to write config file: {e}" ) ) ) ?;
@@ -166,15 +221,26 @@ impl AppConfig {
166221 }
167222
168223 pub fn get_config_path ( ) -> PathBuf {
224+ Self :: get_config_path_with_format ( ConfigFormat :: Toml )
225+ }
226+
227+ pub fn get_config_path_with_format ( format : ConfigFormat ) -> PathBuf {
228+ let extension = match format {
229+ ConfigFormat :: Json => "json" ,
230+ ConfigFormat :: Toml => "toml" ,
231+ } ;
232+
169233 // Check for test environment override
170234 if let Ok ( test_config_dir) = std:: env:: var ( "PULSEPRINT_TEST_CONFIG_DIR" ) {
171- return PathBuf :: from ( test_config_dir) . join ( "config.json" ) ;
235+ return PathBuf :: from ( test_config_dir) . join ( format ! ( "config.{extension}" ) ) ;
172236 }
173237
174238 if let Some ( config_dir) = dirs:: config_dir ( ) {
175- config_dir. join ( "pulseprint-cli" ) . join ( "config.json" )
239+ config_dir
240+ . join ( "pulseprint-cli" )
241+ . join ( format ! ( "config.{extension}" ) )
176242 } else {
177- PathBuf :: from ( ".pulseprint-config.json" )
243+ PathBuf :: from ( format ! ( ".pulseprint-config.{extension}" ) )
178244 }
179245 }
180246}
0 commit comments