@@ -20,72 +20,101 @@ pub struct Config {
2020 pub rabbitmq_exchange_name : String ,
2121}
2222
23- impl TryFrom < JsonConfig > for Config {
23+ impl TryFrom < TomlConfig > for Config {
2424 type Error = io:: Error ;
2525
26- fn try_from ( json_config : JsonConfig ) -> io:: Result < Self > {
26+ fn try_from ( toml_config : TomlConfig ) -> io:: Result < Self > {
2727 let listening_addr =
28- SocketAddress :: from_str ( & json_config . listening_address ) . map_err ( |e| {
28+ SocketAddress :: from_str ( & toml_config . node . listening_address ) . map_err ( |e| {
2929 io:: Error :: new (
3030 io:: ErrorKind :: InvalidInput ,
3131 format ! ( "Invalid listening address configured: {}" , e) ,
3232 )
3333 } ) ?;
34- let rest_service_addr =
35- SocketAddr :: from_str ( & json_config . rest_service_address ) . map_err ( |e| {
34+ let rest_service_addr = SocketAddr :: from_str ( & toml_config . node . rest_service_address )
35+ . map_err ( |e| {
3636 io:: Error :: new (
3737 io:: ErrorKind :: InvalidInput ,
3838 format ! ( "Invalid rest service address configured: {}" , e) ,
3939 )
4040 } ) ?;
41-
4241 let bitcoind_rpc_addr =
43- SocketAddr :: from_str ( & json_config . bitcoind_rpc_address ) . map_err ( |e| {
42+ SocketAddr :: from_str ( & toml_config . bitcoind . rpc_address ) . map_err ( |e| {
4443 io:: Error :: new (
4544 io:: ErrorKind :: InvalidInput ,
4645 format ! ( "Invalid bitcoind RPC address configured: {}" , e) ,
4746 )
4847 } ) ?;
4948
50- #[ cfg( feature = "events-rabbitmq" ) ]
51- if json_config. rabbitmq_connection_string . as_deref ( ) . map_or ( true , |s| s. is_empty ( ) )
52- || json_config. rabbitmq_exchange_name . as_deref ( ) . map_or ( true , |s| s. is_empty ( ) )
53- {
54- return Err ( io:: Error :: new (
55- io:: ErrorKind :: InvalidInput ,
56- "Both `rabbitmq_connection_string` and `rabbitmq_exchange_name` must be configured if enabling `events-rabbitmq` feature." . to_string ( ) ,
57- ) ) ;
58- }
49+ let ( rabbitmq_connection_string, rabbitmq_exchange_name) = {
50+ let rabbitmq = toml_config. rabbitmq . unwrap_or ( RabbitmqConfig {
51+ connection_string : String :: new ( ) ,
52+ exchange_name : String :: new ( ) ,
53+ } ) ;
54+ #[ cfg( feature = "events-rabbitmq" ) ]
55+ if rabbitmq. connection_string . is_empty ( ) || rabbitmq. exchange_name . is_empty ( ) {
56+ return Err ( io:: Error :: new (
57+ io:: ErrorKind :: InvalidInput ,
58+ "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." . to_string ( ) ,
59+ ) ) ;
60+ }
61+ ( rabbitmq. connection_string , rabbitmq. exchange_name )
62+ } ;
5963
6064 Ok ( Config {
6165 listening_addr,
62- network : json_config . network ,
66+ network : toml_config . node . network ,
6367 rest_service_addr,
64- storage_dir_path : json_config . storage_dir_path ,
68+ storage_dir_path : toml_config . storage . disk . dir_path ,
6569 bitcoind_rpc_addr,
66- bitcoind_rpc_user : json_config . bitcoind_rpc_user ,
67- bitcoind_rpc_password : json_config . bitcoind_rpc_password ,
68- rabbitmq_connection_string : json_config . rabbitmq_connection_string . unwrap_or_default ( ) ,
69- rabbitmq_exchange_name : json_config . rabbitmq_exchange_name . unwrap_or_default ( ) ,
70+ bitcoind_rpc_user : toml_config . bitcoind . rpc_user ,
71+ bitcoind_rpc_password : toml_config . bitcoind . rpc_password ,
72+ rabbitmq_connection_string,
73+ rabbitmq_exchange_name,
7074 } )
7175 }
7276}
7377
74- /// Configuration loaded from a JSON file.
78+ /// Configuration loaded from a TOML file.
7579#[ derive( Deserialize , Serialize ) ]
76- pub struct JsonConfig {
77- listening_address : String ,
80+ pub struct TomlConfig {
81+ node : NodeConfig ,
82+ storage : StorageConfig ,
83+ bitcoind : BitcoindConfig ,
84+ rabbitmq : Option < RabbitmqConfig > ,
85+ }
86+
87+ #[ derive( Deserialize , Serialize ) ]
88+ struct NodeConfig {
7889 network : Network ,
90+ listening_address : String ,
7991 rest_service_address : String ,
80- storage_dir_path : String ,
81- bitcoind_rpc_address : String ,
82- bitcoind_rpc_user : String ,
83- bitcoind_rpc_password : String ,
84- rabbitmq_connection_string : Option < String > ,
85- rabbitmq_exchange_name : Option < String > ,
8692}
8793
88- /// Loads the configuration from a JSON file at the given path.
94+ #[ derive( Deserialize , Serialize ) ]
95+ struct StorageConfig {
96+ disk : DiskConfig ,
97+ }
98+
99+ #[ derive( Deserialize , Serialize ) ]
100+ struct DiskConfig {
101+ dir_path : String ,
102+ }
103+
104+ #[ derive( Deserialize , Serialize ) ]
105+ struct BitcoindConfig {
106+ rpc_address : String ,
107+ rpc_user : String ,
108+ rpc_password : String ,
109+ }
110+
111+ #[ derive( Deserialize , Serialize ) ]
112+ struct RabbitmqConfig {
113+ connection_string : String ,
114+ exchange_name : String ,
115+ }
116+
117+ /// Loads the configuration from a TOML file at the given path.
89118pub fn load_config < P : AsRef < Path > > ( config_path : P ) -> io:: Result < Config > {
90119 let file_contents = fs:: read_to_string ( config_path. as_ref ( ) ) . map_err ( |e| {
91120 io:: Error :: new (
@@ -94,21 +123,13 @@ pub fn load_config<P: AsRef<Path>>(config_path: P) -> io::Result<Config> {
94123 )
95124 } ) ?;
96125
97- let json_string = remove_json_comments ( file_contents. as_str ( ) ) ;
98- let json_config: JsonConfig = serde_json:: from_str ( & json_string) . map_err ( |e| {
126+ let toml_config: TomlConfig = toml:: from_str ( & file_contents) . map_err ( |e| {
99127 io:: Error :: new (
100128 io:: ErrorKind :: InvalidData ,
101- format ! ( "Config file contains invalid JSON format: {}" , e) ,
129+ format ! ( "Config file contains invalid TOML format: {}" , e) ,
102130 )
103131 } ) ?;
104- Ok ( Config :: try_from ( json_config) ?)
105- }
106-
107- fn remove_json_comments ( s : & str ) -> String {
108- s. lines ( )
109- . map ( |line| if let Some ( pos) = line. find ( "//" ) { & line[ ..pos] } else { line } )
110- . collect :: < Vec < & str > > ( )
111- . join ( "\n " )
132+ Ok ( Config :: try_from ( toml_config) ?)
112133}
113134
114135#[ cfg( test) ]
@@ -118,25 +139,30 @@ mod tests {
118139 use std:: str:: FromStr ;
119140
120141 #[ test]
121- fn test_read_json_config_from_file ( ) {
142+ fn test_read_toml_config_from_file ( ) {
122143 let storage_path = std:: env:: temp_dir ( ) ;
123- let config_file_name = "config.json" ;
124-
125- let json_config = r#"{
126- "listening_address": "localhost:3001",
127- "network": "regtest",
128- "rest_service_address": "127.0.0.1:3002",
129- "storage_dir_path": "/tmp",
130- "bitcoind_rpc_address":"127.0.0.1:8332", // comment-1
131- "bitcoind_rpc_user": "bitcoind-testuser",
132- "bitcoind_rpc_password": "bitcoind-testpassword",
133- "rabbitmq_connection_string": "rabbitmq_connection_string",
134- "rabbitmq_exchange_name": "rabbitmq_exchange_name",
135- "unknown_key": "random-value"
136- // comment-2
137- }"# ;
138-
139- fs:: write ( storage_path. join ( config_file_name) , json_config) . unwrap ( ) ;
144+ let config_file_name = "config.toml" ;
145+
146+ let toml_config = r#"
147+ [node]
148+ network = "regtest"
149+ listening_address = "localhost:3001"
150+ rest_service_address = "127.0.0.1:3002"
151+
152+ [storage.disk]
153+ dir_path = "/tmp"
154+
155+ [bitcoind]
156+ rpc_address = "127.0.0.1:8332" # RPC endpoint
157+ rpc_user = "bitcoind-testuser"
158+ rpc_password = "bitcoind-testpassword"
159+
160+ [rabbitmq]
161+ connection_string = "rabbitmq_connection_string"
162+ exchange_name = "rabbitmq_exchange_name"
163+ "# ;
164+
165+ fs:: write ( storage_path. join ( config_file_name) , toml_config) . unwrap ( ) ;
140166
141167 assert_eq ! (
142168 load_config( storage_path. join( config_file_name) ) . unwrap( ) ,
0 commit comments