101101// would prevent any parallelism between driver calls, which is not desirable.
102102
103103pub mod error;
104+ pub mod profile;
104105pub ( crate ) mod search;
105106
106107use std:: collections:: HashSet ;
@@ -127,7 +128,10 @@ use adbc_core::{
127128} ;
128129use adbc_ffi:: driver_method;
129130
130- use self :: search:: { parse_driver_uri, DriverLibrary } ;
131+ use self :: search:: { parse_driver_uri, DriverLibrary , DriverLocator } ;
132+ use crate :: profile:: {
133+ process_profile_value, ConnectionProfile , ConnectionProfileProvider , FilesystemProfileProvider ,
134+ } ;
131135
132136const ERR_CANCEL_UNSUPPORTED : & str =
133137 "Canceling connection or statement is not supported with ADBC 1.0.0" ;
@@ -354,6 +358,67 @@ pub struct ManagedDatabase {
354358}
355359
356360impl ManagedDatabase {
361+ /// Creates a new database connection from a URI string.
362+ ///
363+ /// This method supports both direct driver URIs and profile references.
364+ ///
365+ /// # URI Formats
366+ ///
367+ /// ## Direct Driver Connection
368+ /// - `"driver_name:connection_string"` - Loads the specified driver and connects
369+ /// - `"driver_name://host:port/database"` - Standard database URI format
370+ ///
371+ /// ## Profile Reference
372+ /// - `"profile://name"` - Loads connection configuration from a profile file
373+ /// - `"profile:///absolute/path/to/profile.toml"` - Absolute path to profile
374+ /// - `"profile://relative/path/to/profile.toml"` - Relative path to profile
375+ ///
376+ /// # Arguments
377+ ///
378+ /// * `uri` - The connection URI or profile reference
379+ /// * `entrypoint` - Optional driver entrypoint name (uses default if `None`)
380+ /// * `version` - ADBC version to use
381+ /// * `load_flags` - Flags controlling driver loading behavior
382+ /// * `additional_search_paths` - Optional paths to search for drivers or profiles
383+ ///
384+ /// # Returns
385+ ///
386+ /// A configured `ManagedDatabase` ready for creating connections.
387+ ///
388+ /// # Errors
389+ ///
390+ /// Returns an error if:
391+ /// - The URI format is invalid
392+ /// - The driver cannot be loaded
393+ /// - The profile cannot be found or parsed
394+ /// - Database initialization fails
395+ ///
396+ /// # Examples
397+ ///
398+ /// ```no_run
399+ /// use adbc_core::options::AdbcVersion;
400+ /// use adbc_driver_manager::ManagedDatabase;
401+ /// use adbc_core::LOAD_FLAG_DEFAULT;
402+ ///
403+ /// // Direct connection
404+ /// let db = ManagedDatabase::from_uri(
405+ /// "sqlite::memory:",
406+ /// None,
407+ /// AdbcVersion::V100,
408+ /// LOAD_FLAG_DEFAULT,
409+ /// None
410+ /// )?;
411+ ///
412+ /// // Profile connection
413+ /// let db = ManagedDatabase::from_uri(
414+ /// "profile://my_database",
415+ /// None,
416+ /// AdbcVersion::V100,
417+ /// LOAD_FLAG_DEFAULT,
418+ /// None
419+ /// )?;
420+ /// # Ok::<(), adbc_core::error::Error>(())
421+ /// ```
357422 pub fn from_uri (
358423 uri : & str ,
359424 entrypoint : Option < & [ u8 ] > ,
@@ -371,6 +436,53 @@ impl ManagedDatabase {
371436 )
372437 }
373438
439+ /// Creates a new database connection from a URI with additional options.
440+ ///
441+ /// This is similar to [`from_uri`](Self::from_uri), but allows passing additional
442+ /// database options that override any options from profiles.
443+ ///
444+ /// # Arguments
445+ ///
446+ /// * `uri` - The connection URI or profile reference
447+ /// * `entrypoint` - Optional driver entrypoint name
448+ /// * `version` - ADBC version to use
449+ /// * `load_flags` - Flags controlling driver loading behavior
450+ /// * `additional_search_paths` - Optional paths to search for drivers or profiles
451+ /// * `opts` - Database options to apply (override profile options)
452+ ///
453+ /// # Returns
454+ ///
455+ /// A configured `ManagedDatabase` with the specified options applied.
456+ ///
457+ /// # Option Priority
458+ ///
459+ /// Options are applied in this order (later values override earlier ones):
460+ /// 1. Profile options (if using a profile URI)
461+ /// 2. Options provided via `opts` parameter
462+ /// 3. URI connection string (for direct driver URIs)
463+ ///
464+ /// # Examples
465+ ///
466+ /// ```no_run
467+ /// use adbc_core::options::{AdbcVersion, OptionDatabase, OptionValue};
468+ /// use adbc_driver_manager::ManagedDatabase;
469+ /// use adbc_core::LOAD_FLAG_DEFAULT;
470+ ///
471+ /// let opts = vec![
472+ /// (OptionDatabase::Username, OptionValue::String("user".to_string())),
473+ /// (OptionDatabase::Password, OptionValue::String("pass".to_string())),
474+ /// ];
475+ ///
476+ /// let db = ManagedDatabase::from_uri_with_opts(
477+ /// "profile://my_database",
478+ /// None,
479+ /// AdbcVersion::V100,
480+ /// LOAD_FLAG_DEFAULT,
481+ /// None,
482+ /// opts,
483+ /// )?;
484+ /// # Ok::<(), adbc_core::error::Error>(())
485+ /// ```
374486 pub fn from_uri_with_opts (
375487 uri : & str ,
376488 entrypoint : Option < & [ u8 ] > ,
@@ -379,20 +491,121 @@ impl ManagedDatabase {
379491 additional_search_paths : Option < Vec < PathBuf > > ,
380492 opts : impl IntoIterator < Item = ( <Self as Optionable >:: Option , OptionValue ) > ,
381493 ) -> Result < Self > {
382- let ( driver, final_uri) = parse_driver_uri ( uri) ?;
383-
384- let mut drv = ManagedDriver :: load_from_name (
385- driver,
494+ let profile_provider = FilesystemProfileProvider ;
495+ Self :: from_uri_with_profile_provider (
496+ uri,
386497 entrypoint,
387498 version,
388499 load_flags,
389500 additional_search_paths,
390- ) ?;
501+ profile_provider,
502+ opts,
503+ )
504+ }
505+
506+ /// Creates a new database connection from a URI with a custom profile provider.
507+ ///
508+ /// This advanced method allows using a custom implementation of
509+ /// [`ConnectionProfileProvider`] to load profiles from alternative sources
510+ /// (e.g., remote configuration services, encrypted storage, etc.).
511+ ///
512+ /// # Arguments
513+ ///
514+ /// * `uri` - The connection URI or profile reference
515+ /// * `entrypoint` - Optional driver entrypoint name
516+ /// * `version` - ADBC version to use
517+ /// * `load_flags` - Flags controlling driver loading behavior
518+ /// * `additional_search_paths` - Optional paths to search for drivers or profiles
519+ /// * `profile_provider` - Custom profile provider implementation
520+ /// * `opts` - Database options to apply (override profile options)
521+ ///
522+ /// # Returns
523+ ///
524+ /// A configured `ManagedDatabase` using the custom profile provider.
525+ ///
526+ /// # Examples
527+ ///
528+ /// ```no_run
529+ /// use adbc_core::options::{AdbcVersion, OptionDatabase, OptionValue};
530+ /// use adbc_driver_manager::ManagedDatabase;
531+ /// use adbc_driver_manager::profile::FilesystemProfileProvider;
532+ /// use adbc_core::LOAD_FLAG_DEFAULT;
533+ ///
534+ /// let provider = FilesystemProfileProvider;
535+ /// let opts = vec![(OptionDatabase::Username, OptionValue::String("admin".to_string()))];
536+ ///
537+ /// let db = ManagedDatabase::from_uri_with_profile_provider(
538+ /// "profile://my_database",
539+ /// None,
540+ /// AdbcVersion::V100,
541+ /// LOAD_FLAG_DEFAULT,
542+ /// None,
543+ /// provider,
544+ /// opts,
545+ /// )?;
546+ /// # Ok::<(), adbc_core::error::Error>(())
547+ /// ```
548+ pub fn from_uri_with_profile_provider (
549+ uri : & str ,
550+ entrypoint : Option < & [ u8 ] > ,
551+ version : AdbcVersion ,
552+ load_flags : LoadFlags ,
553+ additional_search_paths : Option < Vec < PathBuf > > ,
554+ profile_provider : impl ConnectionProfileProvider ,
555+ opts : impl IntoIterator < Item = ( <Self as Optionable >:: Option , OptionValue ) > ,
556+ ) -> Result < Self > {
557+ let result = parse_driver_uri ( uri) ?;
558+ let ( mut drv, default_opts) = match result {
559+ DriverLocator :: Uri ( driver, final_uri) => {
560+ let drv = ManagedDriver :: load_from_name (
561+ driver,
562+ entrypoint,
563+ version,
564+ load_flags,
565+ additional_search_paths,
566+ ) ?;
567+
568+ let final_opts = vec ! [ (
569+ OptionDatabase :: Uri ,
570+ OptionValue :: String ( final_uri. to_string( ) ) ,
571+ ) ] ;
572+ ( drv, final_opts)
573+ }
574+ DriverLocator :: Profile ( profile) => {
575+ let profile =
576+ profile_provider. get_profile ( profile, additional_search_paths. clone ( ) ) ?;
577+ let ( driver_name, init_func) = profile. get_driver_name ( ) ?;
578+
579+ let drv: ManagedDriver ;
580+ if let Some ( init_fn) = init_func {
581+ drv = ManagedDriver :: load_static ( init_fn, version) ?;
582+ } else {
583+ drv = ManagedDriver :: load_from_name (
584+ driver_name,
585+ entrypoint,
586+ version,
587+ load_flags,
588+ additional_search_paths,
589+ ) ?;
590+ }
591+
592+ let profile_opts: Vec < ( OptionDatabase , OptionValue ) > = profile
593+ . get_options ( ) ?
594+ . into_iter ( )
595+ . map ( |( k, v) | -> Result < ( OptionDatabase , OptionValue ) > {
596+ if let OptionValue :: String ( s) = v {
597+ let result = process_profile_value ( & s) ?;
598+ Ok ( ( k, result) )
599+ } else {
600+ Ok ( ( k, v) )
601+ }
602+ } )
603+ . collect :: < Result < Vec < _ > > > ( ) ?;
604+ ( drv, profile_opts)
605+ }
606+ } ;
391607
392- drv. new_database_with_opts ( opts. into_iter ( ) . chain ( std:: iter:: once ( (
393- OptionDatabase :: Uri ,
394- OptionValue :: String ( final_uri. to_string ( ) ) ,
395- ) ) ) )
608+ drv. new_database_with_opts ( default_opts. into_iter ( ) . chain ( opts) )
396609 }
397610
398611 fn ffi_driver ( & self ) -> & adbc_ffi:: FFI_AdbcDriver {
0 commit comments