Skip to main content

ferro_rs/database/
config.rs

1//! Database configuration for Ferro framework
2
3use crate::config::{env, env_optional};
4
5/// Database type enumeration
6#[derive(Debug, Clone, PartialEq)]
7pub enum DatabaseType {
8    /// PostgreSQL database.
9    Postgres,
10    /// SQLite database.
11    Sqlite,
12    /// Unrecognized database driver.
13    Unknown,
14}
15
16/// Database configuration
17///
18/// # Environment Variables
19///
20/// - `DATABASE_URL` - Full connection URL (required for connection, defaults to sqlite://./database.db)
21/// - `DB_MAX_CONNECTIONS` - Maximum pool connections (default: 10)
22/// - `DB_MIN_CONNECTIONS` - Minimum pool connections (default: 1)
23/// - `DB_CONNECT_TIMEOUT` - Connection timeout in seconds (default: 30)
24/// - `DB_LOGGING` - Enable SQL logging (default: false)
25///
26/// # Example
27///
28/// ```rust,ignore
29/// use ferro_rs::{Config, DatabaseConfig};
30///
31/// // Register from environment
32/// Config::register(DatabaseConfig::from_env());
33///
34/// // Or build manually
35/// Config::register(DatabaseConfig::builder()
36///     .url("postgres://localhost/mydb")
37///     .max_connections(20)
38///     .build());
39/// ```
40#[derive(Debug, Clone)]
41pub struct DatabaseConfig {
42    /// Full database connection URL
43    pub url: String,
44    /// Maximum connections in pool
45    pub max_connections: u32,
46    /// Minimum connections in pool
47    pub min_connections: u32,
48    /// Connection timeout in seconds
49    pub connect_timeout: u64,
50    /// Enable SQL query logging
51    pub logging: bool,
52}
53
54impl DatabaseConfig {
55    /// Create configuration from environment variables
56    pub fn from_env() -> Self {
57        Self {
58            url: env_optional("DATABASE_URL")
59                .unwrap_or_else(|| "sqlite://./database.db".to_string()),
60            max_connections: env("DB_MAX_CONNECTIONS", 10),
61            min_connections: env("DB_MIN_CONNECTIONS", 1),
62            connect_timeout: env("DB_CONNECT_TIMEOUT", 30),
63            logging: env("DB_LOGGING", false),
64        }
65    }
66
67    /// Create a builder for manual configuration
68    pub fn builder() -> DatabaseConfigBuilder {
69        DatabaseConfigBuilder::default()
70    }
71
72    /// Detect database type from URL
73    pub fn database_type(&self) -> DatabaseType {
74        if self.url.starts_with("postgres://") || self.url.starts_with("postgresql://") {
75            DatabaseType::Postgres
76        } else if self.url.starts_with("sqlite://") || self.url.starts_with("sqlite:") {
77            DatabaseType::Sqlite
78        } else {
79            DatabaseType::Unknown
80        }
81    }
82
83    /// Check if database URL is configured (not the default)
84    pub fn is_configured(&self) -> bool {
85        self.url != "sqlite://./database.db"
86    }
87}
88
89impl Default for DatabaseConfig {
90    fn default() -> Self {
91        Self::from_env()
92    }
93}
94
95/// Builder for DatabaseConfig
96#[derive(Debug, Default)]
97pub struct DatabaseConfigBuilder {
98    url: Option<String>,
99    max_connections: Option<u32>,
100    min_connections: Option<u32>,
101    connect_timeout: Option<u64>,
102    logging: Option<bool>,
103}
104
105impl DatabaseConfigBuilder {
106    /// Set the database URL
107    pub fn url(mut self, url: impl Into<String>) -> Self {
108        self.url = Some(url.into());
109        self
110    }
111
112    /// Set maximum pool connections
113    pub fn max_connections(mut self, count: u32) -> Self {
114        self.max_connections = Some(count);
115        self
116    }
117
118    /// Set minimum pool connections
119    pub fn min_connections(mut self, count: u32) -> Self {
120        self.min_connections = Some(count);
121        self
122    }
123
124    /// Set connection timeout in seconds
125    pub fn connect_timeout(mut self, seconds: u64) -> Self {
126        self.connect_timeout = Some(seconds);
127        self
128    }
129
130    /// Enable or disable SQL logging
131    pub fn logging(mut self, enabled: bool) -> Self {
132        self.logging = Some(enabled);
133        self
134    }
135
136    /// Build the configuration
137    pub fn build(self) -> DatabaseConfig {
138        let defaults = DatabaseConfig::from_env();
139        DatabaseConfig {
140            url: self.url.unwrap_or(defaults.url),
141            max_connections: self.max_connections.unwrap_or(defaults.max_connections),
142            min_connections: self.min_connections.unwrap_or(defaults.min_connections),
143            connect_timeout: self.connect_timeout.unwrap_or(defaults.connect_timeout),
144            logging: self.logging.unwrap_or(defaults.logging),
145        }
146    }
147}