@@ -114,32 +114,70 @@ impl Default for HeartBeat {
114114 Self :: new ( )
115115 }
116116}
117-
117+ /// Simple wrapper around Database URL string to provide
118+ /// url constraints and masking functionality.
118119#[ derive( Clone ) ]
119120pub struct DatabaseURL ( String ) ;
120121
121122impl From < & str > for DatabaseURL {
122123 fn from ( s : & str ) -> Self {
123- Self ( s. to_owned ( ) )
124+ let url: Self = Self ( s. to_owned ( ) ) ;
125+ url. parse ( ) . expect ( "DatabaseURL is valid" ) ;
126+ url
124127 }
125128}
126129impl From < String > for DatabaseURL {
127130 fn from ( s : String ) -> Self {
128- Self ( s)
131+ let url: Self = Self ( s) ;
132+ url. parse ( ) . expect ( "DatabaseURL is valid" ) ;
133+ url
129134 }
130135}
131136
132137impl Default for DatabaseURL {
133138 fn default ( ) -> Self {
134- std:: env:: var ( "DATABASE_URL" )
135- . unwrap_or ( "postgres://postgres:postgres@localhost:5432/coprocessor" . to_owned ( ) )
136- . into ( )
139+ let url = std:: env:: var ( "DATABASE_URL" )
140+ . unwrap_or ( "postgres://postgres:postgres@localhost:5432/coprocessor" . to_owned ( ) ) ;
141+
142+ let app_name = Self :: default_app_name ( ) ;
143+ Self :: new_with_app_name ( & url, & app_name)
137144 }
138145}
139146
140147impl DatabaseURL {
148+ /// Create a new DatabaseURL, appending application_name if not present
149+ /// If the base URL already contains an application_name, it will be preserved.
150+ ///
151+ /// application_name is useful for identifying the source of DB conns
152+ pub fn new_with_app_name ( base : & str , app_name : & str ) -> Self {
153+ // Append application_name if not present
154+ let mut url = base. to_owned ( ) ;
155+ if !url. contains ( "application_name=" ) {
156+ if url. contains ( '?' ) {
157+ url. push_str ( & format ! ( "&application_name={}" , app_name) ) ;
158+ } else {
159+ url. push_str ( & format ! ( "?application_name={}" , app_name) ) ;
160+ }
161+ }
162+ let url: Self = Self ( url) ;
163+ let _ = url. parse ( ) . expect ( "DatabaseURL is valid" ) ;
164+ url
165+ }
166+
167+ /// Get default app name from the executable name
168+ fn default_app_name ( ) -> String {
169+ std:: env:: args ( )
170+ . next ( )
171+ . and_then ( |path| {
172+ std:: path:: Path :: new ( & path)
173+ . file_name ( )
174+ . map ( |s| s. to_string_lossy ( ) . into_owned ( ) )
175+ } )
176+ . unwrap_or_else ( || "unknown" . to_string ( ) )
177+ }
178+
141179 pub fn as_str ( & self ) -> & str {
142- & self . 0
180+ self . 0 . as_str ( )
143181 }
144182
145183 pub fn into_inner ( self ) -> String {
@@ -148,15 +186,20 @@ impl DatabaseURL {
148186
149187 fn mask_password ( options : & PgConnectOptions ) -> String {
150188 let new_url = format ! (
151- "postgres://{}:{}@{}:{}/{}" ,
189+ "postgres://{}:{}@{}:{}/{}?application_name={} " ,
152190 options. get_username( ) ,
153191 "*****" ,
154192 options. get_host( ) ,
155193 options. get_port( ) ,
156- options. get_database( ) . unwrap_or_default( )
194+ options. get_database( ) . unwrap_or_default( ) ,
195+ options. get_application_name( ) . unwrap_or_default( )
157196 ) ;
158197 new_url
159198 }
199+
200+ pub fn parse ( & self ) -> Result < PgConnectOptions , sqlx:: Error > {
201+ PgConnectOptions :: from_str ( self . as_str ( ) )
202+ }
160203}
161204
162205impl fmt:: Display for DatabaseURL {
0 commit comments