@@ -17,7 +17,7 @@ pub enum QuerySource {
1717#[ derive( Debug , Clone ) ]
1818pub enum ConnectionSource {
1919 Direct {
20- driver_name : String ,
20+ driver_name : Option < String > ,
2121 uri : Option < String > ,
2222 username : Option < String > ,
2323 password : Option < String > ,
@@ -52,7 +52,7 @@ pub fn parse_args() -> AppConfig {
5252 . conflicts_with ( "driver" ) ,
5353 Arg :: new ( "driver" )
5454 . long ( "driver" )
55- . help ( "Driver name (required if --profile not specified)" ) ,
55+ . help ( "Driver name (inferred from --uri scheme or profile if not specified)" ) ,
5656 Arg :: new ( "uri" )
5757 . long ( "uri" )
5858 . help ( "Database uniform resource identifier" ) ,
@@ -123,14 +123,28 @@ pub fn parse_args() -> AppConfig {
123123 }
124124 } else if let Some ( driver_name) = driver_name {
125125 ConnectionSource :: Direct {
126- driver_name,
126+ driver_name : Some ( driver_name) ,
127+ uri,
128+ username,
129+ password,
130+ options,
131+ }
132+ } else if uri. as_deref ( ) . is_some_and ( uri_has_driver_scheme) {
133+ // The driver can be inferred from the URI scheme (e.g. `--uri duckdb://...`
134+ // implies the `duckdb` driver), so neither --driver nor --profile is
135+ // required. The raw URI is handed to `ManagedDatabase::from_uri`, which
136+ // performs the scheme parsing and driver selection.
137+ ConnectionSource :: Direct {
138+ driver_name : None ,
127139 uri,
128140 username,
129141 password,
130142 options,
131143 }
132144 } else {
133- eprintln ! ( "Error: Either --profile or --driver is required" ) ;
145+ eprintln ! (
146+ "Error: Provide --driver, --profile, or a --uri with a scheme (e.g. duckdb://...)"
147+ ) ;
134148 exit ( 1 ) ;
135149 } ;
136150
@@ -163,6 +177,13 @@ pub fn parse_args() -> AppConfig {
163177 }
164178}
165179
180+ fn uri_has_driver_scheme ( uri : & str ) -> bool {
181+ let Some ( idx) = uri. find ( ':' ) else {
182+ return false ;
183+ } ;
184+ idx != 0
185+ }
186+
166187fn parse_option ( option : & str ) -> Result < ( String , String ) , String > {
167188 let parts: Vec < & str > = option. splitn ( 2 , '=' ) . collect ( ) ;
168189 if parts. len ( ) != 2 {
@@ -184,15 +205,15 @@ mod tests {
184205 #[ test]
185206 fn test_connection_source_direct ( ) {
186207 let source = ConnectionSource :: Direct {
187- driver_name : "duckdb" . to_string ( ) ,
208+ driver_name : Some ( "duckdb" . to_string ( ) ) ,
188209 uri : Some ( ":memory:" . to_string ( ) ) ,
189210 username : None ,
190211 password : None ,
191212 options : vec ! [ ] ,
192213 } ;
193214 match source {
194215 ConnectionSource :: Direct { driver_name, .. } => {
195- assert_eq ! ( driver_name, "duckdb" ) ;
216+ assert_eq ! ( driver_name, Some ( "duckdb" . to_string ( ) ) ) ;
196217 }
197218 _ => panic ! ( "Expected Direct variant" ) ,
198219 }
@@ -216,6 +237,36 @@ mod tests {
216237 }
217238 }
218239
240+ #[ test]
241+ fn test_uri_has_driver_scheme_with_scheme ( ) {
242+ assert ! ( uri_has_driver_scheme( "duckdb://:memory:" ) ) ;
243+ assert ! ( uri_has_driver_scheme( "postgresql://host:5432/db" ) ) ;
244+ assert ! ( uri_has_driver_scheme( "sqlite:file::memory:" ) ) ;
245+ assert ! ( uri_has_driver_scheme( "sqlite::memory:" ) ) ;
246+ // Bare `driver:` form: accepted, matching `parse_driver_uri`.
247+ assert ! ( uri_has_driver_scheme( "duckdb:" ) ) ;
248+ }
249+
250+ #[ test]
251+ fn test_uri_has_driver_scheme_arbitrary_scheme ( ) {
252+ assert ! ( uri_has_driver_scheme( "DuckDB://x" ) ) ;
253+ }
254+
255+ #[ test]
256+ fn test_uri_has_driver_scheme_no_scheme ( ) {
257+ // No colon at all.
258+ assert ! ( !uri_has_driver_scheme( "plain_path" ) ) ;
259+ // Empty scheme (leading colon).
260+ assert ! ( !uri_has_driver_scheme( ":memory:" ) ) ;
261+ }
262+
263+ #[ test]
264+ fn test_uri_has_driver_scheme_accepts_profile_scheme ( ) {
265+ // `profile://` is a valid scheme; `from_uri` resolves it to a profile.
266+ assert ! ( uri_has_driver_scheme( "profile://my_database" ) ) ;
267+ assert ! ( uri_has_driver_scheme( "PROFILE://my_database" ) ) ;
268+ }
269+
219270 #[ test]
220271 fn test_connection_source_profile_with_path ( ) {
221272 let source = ConnectionSource :: Profile {
@@ -272,7 +323,7 @@ mod tests {
272323 fn test_app_config_with_direct_connection ( ) {
273324 let config = AppConfig {
274325 connection : ConnectionSource :: Direct {
275- driver_name : "test_driver" . to_string ( ) ,
326+ driver_name : Some ( "test_driver" . to_string ( ) ) ,
276327 uri : Some ( "test_uri" . to_string ( ) ) ,
277328 username : Some ( "test_user" . to_string ( ) ) ,
278329 password : Some ( "test_pass" . to_string ( ) ) ,
@@ -291,7 +342,7 @@ mod tests {
291342 password,
292343 options,
293344 } => {
294- assert_eq ! ( driver_name, "test_driver" ) ;
345+ assert_eq ! ( driver_name, & Some ( "test_driver" . to_string ( ) ) ) ;
295346 assert_eq ! ( * uri, Some ( "test_uri" . to_string( ) ) ) ;
296347 assert_eq ! ( * username, Some ( "test_user" . to_string( ) ) ) ;
297348 assert_eq ! ( * password, Some ( "test_pass" . to_string( ) ) ) ;
0 commit comments