@@ -19,13 +19,14 @@ use camino::Utf8PathBuf;
19
19
use rand:: Rng ;
20
20
use schemars:: JsonSchema ;
21
21
use serde:: { Deserialize , Serialize } ;
22
- use serde_with:: { serde_as, skip_serializing_none } ;
22
+ use serde_with:: serde_as;
23
23
24
24
use super :: ConfigurationSection ;
25
25
use crate :: schema;
26
26
27
- fn default_connection_string ( ) -> String {
28
- "postgresql://" . to_owned ( )
27
+ #[ allow( clippy:: unnecessary_wraps) ]
28
+ fn default_connection_string ( ) -> Option < String > {
29
+ Some ( "postgresql://" . to_owned ( ) )
29
30
}
30
31
31
32
fn default_max_connections ( ) -> NonZeroU32 {
@@ -49,7 +50,13 @@ fn default_max_lifetime() -> Option<Duration> {
49
50
impl Default for DatabaseConfig {
50
51
fn default ( ) -> Self {
51
52
Self {
52
- options : ConnectConfig :: default ( ) ,
53
+ uri : default_connection_string ( ) ,
54
+ host : None ,
55
+ port : None ,
56
+ socket : None ,
57
+ username : None ,
58
+ password : None ,
59
+ database : None ,
53
60
max_connections : default_max_connections ( ) ,
54
61
min_connections : Default :: default ( ) ,
55
62
connect_timeout : default_connect_timeout ( ) ,
@@ -59,63 +66,56 @@ impl Default for DatabaseConfig {
59
66
}
60
67
}
61
68
62
- /// Database connection configuration
63
- #[ derive( Debug , Serialize , Deserialize , JsonSchema , PartialEq , Eq ) ]
64
- #[ serde( untagged) ]
65
- pub enum ConnectConfig {
66
- /// Connect via a full URI
67
- Uri {
68
- /// Connection URI
69
- #[ schemars( url, default = "default_connection_string" ) ]
70
- uri : String ,
71
- } ,
72
- /// Connect via a map of options
73
- Options {
74
- /// Name of host to connect to
75
- #[ schemars( schema_with = "schema::hostname" ) ]
76
- #[ serde( default ) ]
77
- host : Option < String > ,
78
-
79
- /// Port number to connect at the server host
80
- #[ schemars( schema_with = "schema::port" ) ]
81
- #[ serde( default ) ]
82
- port : Option < u16 > ,
83
-
84
- /// Directory containing the UNIX socket to connect to
85
- #[ serde( default ) ]
86
- #[ schemars( with = "Option<String>" ) ]
87
- socket : Option < Utf8PathBuf > ,
88
-
89
- /// PostgreSQL user name to connect as
90
- #[ serde( default ) ]
91
- username : Option < String > ,
92
-
93
- /// Password to be used if the server demands password authentication
94
- #[ serde( default ) ]
95
- password : Option < String > ,
96
-
97
- /// The database name
98
- #[ serde( default ) ]
99
- database : Option < String > ,
100
- } ,
101
- }
102
-
103
- impl Default for ConnectConfig {
104
- fn default ( ) -> Self {
105
- Self :: Uri {
106
- uri : default_connection_string ( ) ,
107
- }
108
- }
109
- }
110
-
111
69
/// Database connection configuration
112
70
#[ serde_as]
113
- #[ skip_serializing_none]
114
71
#[ derive( Debug , Serialize , Deserialize , JsonSchema ) ]
115
72
pub struct DatabaseConfig {
116
- /// Options related to how to connect to the database
117
- #[ serde( default , flatten) ]
118
- pub options : ConnectConfig ,
73
+ /// Connection URI
74
+ ///
75
+ /// This must not be specified if `host`, `port`, `socket`, `username`,
76
+ /// `password`, or `database` are specified.
77
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
78
+ #[ schemars( url, default = "default_connection_string" ) ]
79
+ pub uri : Option < String > ,
80
+
81
+ /// Name of host to connect to
82
+ ///
83
+ /// This must not be specified if `uri` is specified.
84
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
85
+ #[ schemars( with = "Option::<schema::Hostname>" ) ]
86
+ pub host : Option < String > ,
87
+
88
+ /// Port number to connect at the server host
89
+ ///
90
+ /// This must not be specified if `uri` is specified.
91
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
92
+ #[ schemars( range( min = 1 , max = 65535 ) ) ]
93
+ pub port : Option < u16 > ,
94
+
95
+ /// Directory containing the UNIX socket to connect to
96
+ ///
97
+ /// This must not be specified if `uri` is specified.
98
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
99
+ #[ schemars( with = "Option<String>" ) ]
100
+ pub socket : Option < Utf8PathBuf > ,
101
+
102
+ /// PostgreSQL user name to connect as
103
+ ///
104
+ /// This must not be specified if `uri` is specified.
105
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
106
+ pub username : Option < String > ,
107
+
108
+ /// Password to be used if the server demands password authentication
109
+ ///
110
+ /// This must not be specified if `uri` is specified.
111
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
112
+ pub password : Option < String > ,
113
+
114
+ /// The database name
115
+ ///
116
+ /// This must not be specified if `uri` is specified.
117
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
118
+ pub database : Option < String > ,
119
119
120
120
/// Set the maximum number of connections the pool should maintain
121
121
#[ serde( default = "default_max_connections" ) ]
@@ -133,13 +133,19 @@ pub struct DatabaseConfig {
133
133
134
134
/// Set a maximum idle duration for individual connections
135
135
#[ schemars( with = "Option<u64>" ) ]
136
- #[ serde( default = "default_idle_timeout" ) ]
136
+ #[ serde(
137
+ default = "default_idle_timeout" ,
138
+ skip_serializing_if = "Option::is_none"
139
+ ) ]
137
140
#[ serde_as( as = "Option<serde_with::DurationSeconds<u64>>" ) ]
138
141
pub idle_timeout : Option < Duration > ,
139
142
140
143
/// Set the maximum lifetime of individual connections
141
144
#[ schemars( with = "u64" ) ]
142
- #[ serde( default = "default_max_lifetime" ) ]
145
+ #[ serde(
146
+ default = "default_max_lifetime" ,
147
+ skip_serializing_if = "Option::is_none"
148
+ ) ]
143
149
#[ serde_as( as = "Option<serde_with::DurationSeconds<u64>>" ) ]
144
150
pub max_lifetime : Option < Duration > ,
145
151
}
@@ -155,6 +161,31 @@ impl ConfigurationSection for DatabaseConfig {
155
161
Ok ( Self :: default ( ) )
156
162
}
157
163
164
+ fn validate ( & self , figment : & figment:: Figment ) -> Result < ( ) , figment:: error:: Error > {
165
+ let metadata = figment. find_metadata ( Self :: PATH . unwrap ( ) ) ;
166
+
167
+ // Check that the user did not specify both `uri` and the split options at the
168
+ // same time
169
+ let has_split_options = self . host . is_some ( )
170
+ || self . port . is_some ( )
171
+ || self . socket . is_some ( )
172
+ || self . username . is_some ( )
173
+ || self . password . is_some ( )
174
+ || self . database . is_some ( ) ;
175
+
176
+ if self . uri . is_some ( ) && has_split_options {
177
+ let mut error = figment:: error:: Error :: from (
178
+ "uri must not be specified if host, port, socket, username, password, or database are specified" . to_owned ( ) ,
179
+ ) ;
180
+ error. metadata = metadata. cloned ( ) ;
181
+ error. profile = Some ( figment:: Profile :: Default ) ;
182
+ error. path = vec ! [ Self :: PATH . unwrap( ) . to_owned( ) , "uri" . to_owned( ) ] ;
183
+ return Err ( error) ;
184
+ }
185
+
186
+ Ok ( ( ) )
187
+ }
188
+
158
189
fn test ( ) -> Self {
159
190
Self :: default ( )
160
191
}
@@ -185,10 +216,8 @@ mod tests {
185
216
. extract_inner :: < DatabaseConfig > ( "database" ) ?;
186
217
187
218
assert_eq ! (
188
- config. options,
189
- ConnectConfig :: Uri {
190
- uri: "postgresql://user:password@host/database" . to_string( )
191
- }
219
+ config. uri. as_deref( ) ,
220
+ Some ( "postgresql://user:password@host/database" )
192
221
) ;
193
222
194
223
Ok ( ( ) )
0 commit comments