@@ -20,72 +20,101 @@ pub struct Config {
20
20
pub rabbitmq_exchange_name : String ,
21
21
}
22
22
23
- impl TryFrom < JsonConfig > for Config {
23
+ impl TryFrom < TomlConfig > for Config {
24
24
type Error = io:: Error ;
25
25
26
- fn try_from ( json_config : JsonConfig ) -> io:: Result < Self > {
26
+ fn try_from ( toml_config : TomlConfig ) -> io:: Result < Self > {
27
27
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| {
29
29
io:: Error :: new (
30
30
io:: ErrorKind :: InvalidInput ,
31
31
format ! ( "Invalid listening address configured: {}" , e) ,
32
32
)
33
33
} ) ?;
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| {
36
36
io:: Error :: new (
37
37
io:: ErrorKind :: InvalidInput ,
38
38
format ! ( "Invalid rest service address configured: {}" , e) ,
39
39
)
40
40
} ) ?;
41
-
42
41
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| {
44
43
io:: Error :: new (
45
44
io:: ErrorKind :: InvalidInput ,
46
45
format ! ( "Invalid bitcoind RPC address configured: {}" , e) ,
47
46
)
48
47
} ) ?;
49
48
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
+ } ;
59
63
60
64
Ok ( Config {
61
65
listening_addr,
62
- network : json_config . network ,
66
+ network : toml_config . node . network ,
63
67
rest_service_addr,
64
- storage_dir_path : json_config . storage_dir_path ,
68
+ storage_dir_path : toml_config . storage . disk . dir_path ,
65
69
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,
70
74
} )
71
75
}
72
76
}
73
77
74
- /// Configuration loaded from a JSON file.
78
+ /// Configuration loaded from a TOML file.
75
79
#[ 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 {
78
89
network : Network ,
90
+ listening_address : String ,
79
91
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 > ,
86
92
}
87
93
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.
89
118
pub fn load_config < P : AsRef < Path > > ( config_path : P ) -> io:: Result < Config > {
90
119
let file_contents = fs:: read_to_string ( config_path. as_ref ( ) ) . map_err ( |e| {
91
120
io:: Error :: new (
@@ -94,21 +123,13 @@ pub fn load_config<P: AsRef<Path>>(config_path: P) -> io::Result<Config> {
94
123
)
95
124
} ) ?;
96
125
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| {
99
127
io:: Error :: new (
100
128
io:: ErrorKind :: InvalidData ,
101
- format ! ( "Config file contains invalid JSON format: {}" , e) ,
129
+ format ! ( "Config file contains invalid TOML format: {}" , e) ,
102
130
)
103
131
} ) ?;
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) ?)
112
133
}
113
134
114
135
#[ cfg( test) ]
@@ -118,25 +139,30 @@ mod tests {
118
139
use std:: str:: FromStr ;
119
140
120
141
#[ test]
121
- fn test_read_json_config_from_file ( ) {
142
+ fn test_read_toml_config_from_file ( ) {
122
143
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 ( ) ;
140
166
141
167
assert_eq ! (
142
168
load_config( storage_path. join( config_file_name) ) . unwrap( ) ,
0 commit comments