dmsc/database/
config.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Database Configuration
19//!
20//! This module provides database configuration types and settings for DMSC.
21//! It supports multiple database backends including MySQL, PostgreSQL, SQLite, and in-memory databases.
22//!
23//! ## Key Components
24//!
25//! - **DMSCDatabaseConfig**: Enum for different database configurations
26//! - **DatabaseType**: Enum for supported database engines
27//! - **PoolConfig**: Connection pool configuration settings
28//!
29//! ## Design Principles
30//!
31//! 1. **Type Safety**: Each database type has its own configuration variant
32//! 2. **Flexible Pooling**: Configurable connection pool settings for performance
33//! 3. **Backend-Agnostic**: Unified interface across different database engines
34//! 4. **Default Values**: Sensible defaults for all configuration options
35//!
36//! ## Usage Example
37//!
38//! ```rust,ignore
39//! use dmsc::database::{DMSCDatabaseConfig, DatabaseType, PoolConfig};
40//!
41//! let config = DMSCDatabaseConfig::new_mysql(
42//!     "localhost",
43//!     3306,
44//!     "root",
45//!     "password",
46//!     "test_db",
47//! );
48//!
49//! let pool_config = PoolConfig::new(10, 300, 600);
50//! ```
51
52use serde::{Deserialize, Serialize};
53use std::env;
54
55/// Enumeration of supported database engine types.
56///
57/// This enum represents the different database backends that DMSC can connect to.
58/// Each database type has specific connection requirements and may use different
59/// underlying drivers or client libraries.
60///
61/// ## Currently Implemented
62///
63/// | Database Type | Feature Flag | Status |
64/// |---------------|--------------|--------|
65/// | PostgreSQL | `postgres` | ✅ Available |
66/// | MySQL | `mysql` | ✅ Available |
67/// | SQLite | `sqlite` | ✅ Available |
68/// | MongoDB | `mongodb` | 🔜 Planned |
69/// | Redis | `redis` | 🔜 Planned |
70///
71/// ## Roadmap
72///
73/// MongoDB and Redis support are planned for future releases. The enum variants
74/// are reserved to maintain API stability when these features are added.
75///
76/// ## Usage
77///
78/// ```rust,ignore
79/// use dmsc::database::DatabaseType;
80///
81/// fn get_preferred_db() -> DatabaseType {
82///     DatabaseType::Postgres
83/// }
84/// ```
85#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
87pub enum DatabaseType {
88    /// PostgreSQL database engine.
89    ///
90    /// PostgreSQL is a powerful, open source object-relational database system.
91    /// It is known for its reliability, feature richness, and performance.
92    /// Default port is 5432.
93    ///
94    /// ## Features
95    ///
96    /// - Full ACID compliance
97    /// - Complex queries and joins
98    /// - Foreign key support
99    /// - Triggers and views
100    /// - Stored procedures
101    Postgres,
102    /// MySQL database engine.
103    ///
104    /// MySQL is the world's most popular open source database.
105    /// It is widely used for web applications and is known for its speed and reliability.
106    /// Default port is 3306.
107    ///
108    /// ## Features
109    ///
110    /// - ACID compliance (with InnoDB)
111    /// - Cross-platform support
112    /// - Stored procedures and triggers
113    /// - Full-text indexing
114    MySQL,
115    /// SQLite database engine.
116    ///
117    /// SQLite is a lightweight, file-based database engine.
118    /// It requires no server and is embedded directly into the application.
119    /// Suitable for development, testing, and desktop applications.
120    ///
121    /// ## Features
122    ///
123    /// - Serverless architecture
124    /// - Zero-configuration
125    /// - Single file storage
126    /// - Full SQL support
127    SQLite,
128    /// MongoDB database engine.
129    ///
130    /// MongoDB is a document-oriented NoSQL database.
131    /// It uses JSON-like documents with optional schemas.
132    /// Default port is 27017.
133    ///
134    /// ## Features
135    ///
136    /// - Flexible document schema
137    /// - Horizontal scaling
138    /// - Rich query language
139    /// - Automatic sharding
140    MongoDB,
141    /// Redis database engine.
142    ///
143    /// Redis is an in-memory data structure store.
144    /// It can be used as a database, cache, and message broker.
145    /// Default port is 6379.
146    ///
147    /// ## Features
148    ///
149    /// - In-memory storage
150    /// - Data structures (strings, hashes, lists, sets)
151    /// - Pub/Sub messaging
152    /// - Persistence options
153    Redis,
154}
155
156impl Default for DatabaseType {
157    fn default() -> Self {
158        DatabaseType::Postgres
159    }
160}
161
162impl std::fmt::Display for DatabaseType {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        match self {
165            DatabaseType::Postgres => write!(f, "postgresql"),
166            DatabaseType::MySQL => write!(f, "mysql"),
167            DatabaseType::SQLite => write!(f, "sqlite"),
168            DatabaseType::MongoDB => write!(f, "mongodb"),
169            DatabaseType::Redis => write!(f, "redis"),
170        }
171    }
172}
173
174/// Configuration for database connections in DMSC.
175///
176/// This struct encapsulates all configuration options needed to establish and manage
177/// database connections. It supports multiple database backends through the `DatabaseType`
178/// enum and provides a fluent builder API for configuration.
179///
180/// ## Connection Pooling
181///
182/// DMSC uses connection pooling to efficiently manage database connections.
183/// The pool maintains a set of connections that are reused across requests,
184/// reducing the overhead of establishing new connections.
185///
186/// ## Configuration Methods
187///
188/// The struct provides several factory methods for creating configurations:
189/// - [`postgres()`][DMSCDatabaseConfig::postgres] - PostgreSQL with default settings
190/// - [`mysql()`][DMSCDatabaseConfig::mysql] - MySQL with default settings
191/// - [`sqlite(path)`][DMSCDatabaseConfig::sqlite] - SQLite at specified path
192///
193/// ## Builder Pattern
194///
195/// Configuration can be customized using the builder pattern:
196///
197/// ```rust,ignore
198/// use dmsc::database::{DMSCDatabaseConfig, SslMode};
199///
200/// let config = DMSCDatabaseConfig::postgres()
201///     .host("db.example.com")
202///     .port(5432)
203///     .database("myapp")
204///     .user("admin")
205///     .password("secret")
206///     .max_connections(20)
207///     .ssl_mode(SslMode::Require)
208///     .build();
209/// ```
210///
211/// ## Environment Variables
212///
213/// Default values can be overridden using environment variables:
214/// - `DMSC_DB_HOST` - Database server hostname
215/// - `DMSC_DB_PORT` - Database server port
216/// - `DMSC_DB_NAME` - Database name
217/// - `DMSC_DB_USER` - Database username
218/// - `DMSC_DB_PASSWORD` - Database password
219///
220/// ## Thread Safety
221///
222/// This struct is clonable and can be shared across threads.
223/// However, modifications should be done before the configuration is passed
224/// to the database manager.
225#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct DMSCDatabaseConfig {
228    /// The type of database backend to connect to.
229    ///
230    /// This determines which driver and connection logic will be used.
231    /// Common values are `DatabaseType::Postgres`, `DatabaseType::MySQL`,
232    /// and `DatabaseType::SQLite`.
233    pub database_type: DatabaseType,
234
235    /// Hostname or IP address of the database server.
236    ///
237    /// For local development, this is typically `"localhost"` or `"127.0.0.1"`.
238    /// For production, this should be the database server's hostname.
239    ///
240    /// ## Examples
241    ///
242    /// - `localhost` - Local database server
243    /// - `db.example.com` - Remote database server
244    /// - `192.168.1.100` - IP address of database server
245    pub host: String,
246
247    /// Port number for database connections.
248    ///
249    /// Each database type has a default port:
250    /// - PostgreSQL: 5432
251    /// - MySQL: 3306
252    /// - MongoDB: 27017
253    /// - Redis: 6379
254    ///
255    /// SQLite ignores this field as it uses file-based connections.
256    pub port: u16,
257
258    /// Name of the database to connect to.
259    ///
260    /// For PostgreSQL and MySQL, this is the name of a specific database
261    /// within the database server.
262    ///
263    /// For SQLite, this is the file path (`:memory:` for in-memory database).
264    pub database: String,
265
266    /// Username for database authentication.
267    ///
268    /// This user must have sufficient privileges to perform the required
269    /// database operations. For security, consider using environment variables
270    /// or secrets management to provide this value.
271    pub username: String,
272
273    /// Password for database authentication.
274    ///
275    /// This password is used together with the username to authenticate
276    /// with the database server. For security, consider using environment
277    /// variables or secrets management to provide this value.
278    pub password: String,
279
280    /// Maximum number of concurrent database connections.
281    ///
282    /// This setting controls the upper bound of the connection pool.
283    /// Higher values allow more concurrent database operations but increase
284    /// resource usage on both the application and database server.
285    ///
286    /// ## Recommendations
287    ///
288    /// - Development: 5-10 connections
289    /// - Production: 10-50 connections (depends on workload)
290    /// - Consider database server's max_connections setting
291    pub max_connections: u32,
292
293    /// Minimum number of idle connections to maintain.
294    ///
295    /// The connection pool will maintain at least this many idle connections
296    /// to reduce the latency of new database operations. These connections
297    /// are still subject to the idle timeout.
298    ///
299    /// ## Default Value
300    ///
301    /// Typically 1-2 connections, depending on expected concurrency.
302    pub min_idle_connections: u32,
303
304    /// Timeout for establishing new connections in seconds.
305    ///
306    /// If a connection cannot be established within this time, the operation
307    /// will fail with a timeout error. This prevents the application from
308    /// hanging indefinitely when the database is unreachable.
309    ///
310    /// ## Common Values
311    ///
312    /// - 30 seconds for most scenarios
313    /// - 5-10 seconds for latency-sensitive applications
314    /// - 60+ seconds for distant database servers
315    pub connection_timeout_secs: u64,
316
317    /// Maximum time a connection can be idle before being closed.
318    ///
319    /// Idle connections that have not been used for this duration will be
320    /// closed and removed from the pool. This helps free resources on both
321    /// the application and database server.
322    ///
323    /// ## Recommendations
324    ///
325    /// - 600 seconds (10 minutes) for web applications
326    /// - 300 seconds (5 minutes) for batch processing
327    /// - Consider database server's connection timeout settings
328    pub idle_timeout_secs: u64,
329
330    /// Maximum lifetime of a connection in seconds.
331    ///
332    /// Connections older than this will be closed and replaced with new ones.
333    /// This prevents connections from becoming stale due to:
334    /// - Network interruptions
335    /// - Database server restarts
336    /// - Connection timeout on the database side
337    ///
338    /// ## Recommendations
339    ///
340    /// - 1800-3600 seconds (30-60 minutes) for most applications
341    /// - Shorter values for long-running applications
342    /// - Disable (use None) for very short-lived applications
343    pub max_lifetime_secs: u64,
344
345    /// SSL/TLS mode for encrypted connections.
346    ///
347    /// This setting controls whether and how SSL/TLS encryption is used
348    /// for database connections. It is ignored by SQLite.
349    ///
350    /// ## Security
351    ///
352    /// Always use `SslMode::Require` in production environments to ensure
353    /// all database traffic is encrypted.
354    pub ssl_mode: SslMode,
355
356    /// Maximum number of prepared statements to cache.
357    ///
358    /// Prepared statements are cached to reduce the overhead of repeated
359    /// query compilation. Higher values improve performance for complex
360    /// queries but increase memory usage.
361    ///
362    /// ## Recommendations
363    ///
364    /// - 100-500 for typical applications
365    /// - 1000+ for applications with many repeated complex queries
366    /// - 0 to disable statement caching
367    pub statement_cache_size: u32,
368}
369
370#[cfg(feature = "pyo3")]
371#[pyo3::prelude::pymethods]
372impl DMSCDatabaseConfig {
373    #[new]
374    fn py_new(
375        database_type: DatabaseType,
376        host: String,
377        port: u16,
378        database: String,
379        username: String,
380        password: String,
381        max_connections: u32,
382        min_idle_connections: u32,
383        connection_timeout_secs: u64,
384        idle_timeout_secs: u64,
385        max_lifetime_secs: u64,
386        ssl_mode: SslMode,
387        statement_cache_size: u32,
388    ) -> Self {
389        Self {
390            database_type,
391            host,
392            port,
393            database,
394            username,
395            password,
396            max_connections,
397            min_idle_connections,
398            connection_timeout_secs,
399            idle_timeout_secs,
400            max_lifetime_secs,
401            ssl_mode,
402            statement_cache_size,
403        }
404    }
405
406    #[staticmethod]
407    fn create_postgres() -> Self {
408        Self::postgres()
409    }
410
411    #[staticmethod]
412    fn create_mysql() -> Self {
413        Self::mysql()
414    }
415
416    #[staticmethod]
417    fn create_sqlite() -> Self {
418        Self::sqlite(":memory:")
419    }
420
421    fn get_database_type(&self) -> DatabaseType {
422        self.database_type
423    }
424
425    fn set_database_type(&self, _database_type: DatabaseType) {
426        // Can't modify in pyo3, use create functions instead
427    }
428
429    fn get_host(&self) -> String {
430        self.host.clone()
431    }
432
433    fn set_host(&mut self, host: String) {
434        self.host = host;
435    }
436
437    fn get_port(&self) -> u16 {
438        self.port
439    }
440
441    fn set_port(&mut self, port: u16) {
442        self.port = port;
443    }
444
445    fn get_database(&self) -> String {
446        self.database.clone()
447    }
448
449    fn set_database(&mut self, database: String) {
450        self.database = database;
451    }
452
453    fn get_username(&self) -> String {
454        self.username.clone()
455    }
456
457    fn set_username(&mut self, username: String) {
458        self.username = username;
459    }
460
461    fn get_password(&self) -> String {
462        self.password.clone()
463    }
464
465    fn set_password(&mut self, password: String) {
466        self.password = password;
467    }
468
469    fn get_max_connections(&self) -> u32 {
470        self.max_connections
471    }
472
473    fn set_max_connections(&mut self, max_connections: u32) {
474        self.max_connections = max_connections;
475    }
476
477    fn get_min_idle_connections(&self) -> u32 {
478        self.min_idle_connections
479    }
480
481    fn set_min_idle_connections(&mut self, min_idle_connections: u32) {
482        self.min_idle_connections = min_idle_connections;
483    }
484}
485
486/// SSL/TLS connection mode for database connections.
487///
488/// This enum controls whether and how SSL/TLS encryption is used when
489/// connecting to the database. SSL/TLS provides:
490/// - **Confidentiality**: Encryption prevents eavesdropping on database traffic
491/// - **Integrity**: Protection against data tampering during transmission
492/// - **Authentication**: Verification of the database server's identity
493///
494/// ## Security Recommendations
495///
496/// | Environment | Recommended Mode | Reason |
497/// |-------------|------------------|--------|
498/// | Production | `Require` | Maximum security, prevents MITM attacks |
499/// | Development | `Prefer` | Encryption when available |
500/// | Testing | `Prefer` or `Disable` | Convenience during development |
501///
502/// ## Database Support
503///
504/// - **PostgreSQL**: Fully supports SSL with all modes
505/// - **MySQL**: Fully supports SSL with all modes
506/// - **MongoDB**: Supports SSL with all modes
507/// - **SQLite**: Does not support SSL (ignored)
508/// - **Redis**: Uses separate TLS configuration
509///
510/// ## Certificate Verification
511///
512/// When using `Require`, the client will verify the server's certificate.
513/// This requires the server to have a valid certificate signed by a trusted
514/// certificate authority. Self-signed certificates will fail verification.
515///
516/// For development with self-signed certificates, you may need to:
517/// 1. Add the certificate to your system's trust store
518/// 2. Configure the database client to trust the specific certificate
519/// 3. Use `Prefer` mode (less secure, not recommended for production)
520#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
521#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
522pub enum SslMode {
523    /// SSL/TLS is disabled. Connections are unencrypted.
524    ///
525    /// ## Use Cases
526    ///
527    /// - Local development with local database
528    /// - Testing in isolated environments
529    /// - Situations where encryption overhead is unacceptable
530    ///
531    /// ## Security Warning
532    ///
533    /// This mode provides no protection against eavesdropping or tampering.
534    /// Never use in production or when transmitting sensitive data.
535    Disable,
536
537    /// SSL/TLS is preferred but not required.
538    ///
539    /// The client will attempt to establish an SSL connection if the server
540    /// supports it. If SSL is not available, the connection will fall back
541    /// to an unencrypted connection.
542    ///
543    /// ## Behavior
544    ///
545    /// 1. Client requests SSL connection
546    /// 2. If server supports SSL, use encrypted connection
547    /// 3. If server rejects SSL, use unencrypted connection
548    ///
549    /// ## Security Warning
550    ///
551    /// This mode allows fallback to unencrypted connections, which could
552    /// be exploited in man-in-the-middle attacks. Consider using `Require`
553    /// for better security.
554    Prefer,
555
556    /// SSL/TLS is required.
557    ///
558    /// The client will only establish connections that are encrypted with
559    /// SSL/TLS. The connection will fail if SSL is not available or if
560    /// certificate verification fails.
561    ///
562    /// ## Server Certificate Verification
563    ///
564    /// When `Require` mode is used, the client verifies:
565    /// - The certificate is not expired
566    /// - The certificate is signed by a trusted CA
567    /// - The certificate hostname matches the server hostname
568    ///
569    /// ## Use Cases
570    ///
571    /// - Production environments
572    /// - When transmitting sensitive data
573    /// - Compliance with security regulations
574    ///
575    /// ## Common Errors
576    ///
577    /// - `certificate verify failed`: Certificate not trusted
578    /// - `certificate expired`: Certificate has expired
579    /// - `hostname mismatch`: Certificate not issued for this server
580    Require,
581}
582
583impl Default for SslMode {
584    fn default() -> Self {
585        SslMode::Prefer
586    }
587}
588
589impl DMSCDatabaseConfig {
590    /// Creates a configuration for PostgreSQL with default settings.
591    ///
592    /// This factory method initializes a configuration with sensible defaults
593    /// for PostgreSQL connections. Default values can be overridden using
594    /// environment variables or the builder methods.
595    ///
596    /// ## Defaults
597    ///
598    /// - Host: `localhost` (or `DMSC_DB_HOST` env var)
599    /// - Port: `5432` (or `DMSC_DB_PORT` env var)
600    /// - Database: `dmsc` (or `DMSC_DB_NAME` env var)
601    /// - Username: `dmsc` (or `DMSC_DB_USER` env var)
602    /// - Password: empty (or `DMSC_DB_PASSWORD` env var)
603    /// - Max connections: 10
604    /// - Min idle: 2
605    /// - Connection timeout: 30 seconds
606    /// - Idle timeout: 600 seconds
607    /// - Max lifetime: 3600 seconds
608    /// - SSL mode: `Prefer`
609    /// - Statement cache: 100
610    ///
611    /// ## Environment Variable Override
612    ///
613    /// Default values are read from environment variables if available:
614    /// ```bash
615    /// export DMSC_DB_HOST=db.example.com
616    /// export DMSC_DB_PORT=5432
617    /// export DMSC_DB_NAME=myapp
618    /// export DMSC_DB_USER=admin
619    /// export DMSC_DB_PASSWORD=secret
620    /// ```
621    ///
622    /// # Returns
623    ///
624    /// A new `DMSCDatabaseConfig` instance configured for PostgreSQL
625    ///
626    /// # Examples
627    ///
628    /// ```rust,ignore
629    /// use dmsc::database::DMSCDatabaseConfig;
630    ///
631    /// // Basic PostgreSQL configuration
632    /// let config = DMSCDatabaseConfig::postgres();
633    ///
634    /// // With environment variable overrides
635    /// // (assumes DMSC_DB_* variables are set)
636    /// let config = DMSCDatabaseConfig::postgres();
637    /// ```
638    pub fn postgres() -> Self {
639        Self {
640            database_type: DatabaseType::Postgres,
641            host: env::var("DMSC_DB_HOST").unwrap_or_else(|_| "localhost".to_string()),
642            port: env::var("DMSC_DB_PORT")
643                .unwrap_or_else(|_| "5432".to_string())
644                .parse()
645                .unwrap_or(5432),
646            database: env::var("DMSC_DB_NAME").unwrap_or_else(|_| "dmsc".to_string()),
647            username: env::var("DMSC_DB_USER").unwrap_or_else(|_| "dmsc".to_string()),
648            password: env::var("DMSC_DB_PASSWORD").unwrap_or_else(|_| "".to_string()),
649            max_connections: 10,
650            min_idle_connections: 2,
651            connection_timeout_secs: 30,
652            idle_timeout_secs: 600,
653            max_lifetime_secs: 3600,
654            ssl_mode: SslMode::Prefer,
655            statement_cache_size: 100,
656        }
657    }
658
659    /// Creates a configuration for MySQL with default settings.
660    ///
661    /// This factory method initializes a configuration with sensible defaults
662    /// for MySQL connections. Default values can be overridden using
663    /// environment variables or the builder methods.
664    ///
665    /// ## Defaults
666    ///
667    /// - Host: `localhost` (or `DMSC_DB_HOST` env var)
668    /// - Port: `3306` (or `DMSC_DB_PORT` env var)
669    /// - Database: `dmsc` (or `DMSC_DB_NAME` env var)
670    /// - Username: `dmsc` (or `DMSC_DB_USER` env var)
671    /// - Password: empty (or `DMSC_DB_PASSWORD` env var)
672    /// - Max connections: 10
673    /// - Min idle: 2
674    /// - Connection timeout: 30 seconds
675    /// - Idle timeout: 600 seconds
676    /// - Max lifetime: 3600 seconds
677    /// - SSL mode: `Prefer`
678    /// - Statement cache: 100
679    ///
680    /// ## MySQL-Specific Notes
681    ///
682    /// - MySQL uses `mysql://` URI scheme in connection strings
683    /// - MySQL 8.0+ uses `caching_sha2_password` by default
684    /// - Consider using `SslMode::Require` for production
685    ///
686    /// # Returns
687    ///
688    /// A new `DMSCDatabaseConfig` instance configured for MySQL
689    ///
690    /// # Examples
691    ///
692    /// ```rust,ignore
693    /// use dmsc::database::DMSCDatabaseConfig;
694    ///
695    /// // Basic MySQL configuration
696    /// let config = DMSCDatabaseConfig::mysql();
697    ///
698    /// // Customized configuration
699    /// let config = DMSCDatabaseConfig::mysql()
700    ///     .host("db.example.com")
701    ///     .database("myapp")
702    ///     .user("app_user")
703    ///     .password("secure_password");
704    /// ```
705    pub fn mysql() -> Self {
706        Self {
707            database_type: DatabaseType::MySQL,
708            host: env::var("DMSC_DB_HOST").unwrap_or_else(|_| "localhost".to_string()),
709            port: env::var("DMSC_DB_PORT")
710                .unwrap_or_else(|_| "3306".to_string())
711                .parse()
712                .unwrap_or(3306),
713            database: env::var("DMSC_DB_NAME").unwrap_or_else(|_| "dmsc".to_string()),
714            username: env::var("DMSC_DB_USER").unwrap_or_else(|_| "dmsc".to_string()),
715            password: env::var("DMSC_DB_PASSWORD").unwrap_or_else(|_| "".to_string()),
716            max_connections: 10,
717            min_idle_connections: 2,
718            connection_timeout_secs: 30,
719            idle_timeout_secs: 600,
720            max_lifetime_secs: 3600,
721            ssl_mode: SslMode::Prefer,
722            statement_cache_size: 100,
723        }
724    }
725
726    /// Creates a configuration for SQLite at the specified path.
727    ///
728    /// This factory method initializes a configuration for SQLite database
729    /// at the given file path. SQLite is a serverless database that stores
730    /// data in a single file.
731    ///
732    /// ## Special Considerations
733    ///
734    /// - The `host` and `port` fields are ignored
735    /// - The `username` and `password` fields are ignored
736    /// - The `ssl_mode` field is ignored
737    /// - File path can be `:memory:` for in-memory database
738    ///
739    /// ## Path Handling
740    ///
741    /// - Relative paths are resolved relative to the current working directory
742    /// - Parent directories are created automatically if they don't exist
743    /// - Use absolute paths for reliability in production
744    ///
745    /// ## File Permissions
746    ///
747    /// The SQLite file and its directory must be writable by the application.
748    /// Consider the following:
749    /// - The application user needs write permission to the database file
750    /// - The directory containing the database must be writable (for journal files)
751    /// - Consider file permissions (0600 recommended for the database file)
752    ///
753    /// # Arguments
754    ///
755    /// * `path` - File path for the SQLite database (or `:memory:` for in-memory)
756    ///
757    /// # Returns
758    ///
759    /// A new `DMSCDatabaseConfig` instance configured for SQLite
760    ///
761    /// # Examples
762    ///
763    /// ```rust,ignore
764    /// use dmsc::database::DMSCDatabaseConfig;
765    ///
766    /// // File-based database
767    /// let config = DMSCDatabaseConfig::sqlite("./data/myapp.db");
768    ///
769    /// // In-memory database (for testing)
770    /// let config = DMSCDatabaseConfig::sqlite(":memory:");
771    ///
772    /// // Absolute path
773    /// let config = DMSCDatabaseConfig::sqlite("/var/lib/dmsc/database.db");
774    /// ```
775    pub fn sqlite(path: &str) -> Self {
776        Self {
777            database_type: DatabaseType::SQLite,
778            host: "".to_string(),
779            port: 0,
780            database: path.to_string(),
781            username: "".to_string(),
782            password: "".to_string(),
783            max_connections: 10,
784            min_idle_connections: 1,
785            connection_timeout_secs: 30,
786            idle_timeout_secs: 600,
787            max_lifetime_secs: 3600,
788            ssl_mode: SslMode::Disable,
789            statement_cache_size: 100,
790        }
791    }
792
793    /// Sets the database server hostname.
794    ///
795    /// This method configures the host address for database connections.
796    /// It accepts hostnames, domain names, and IP addresses.
797    ///
798    /// # Arguments
799    ///
800    /// * `host` - The hostname or IP address of the database server
801    ///
802    /// # Returns
803    ///
804    /// The updated configuration (for method chaining)
805    ///
806    /// # Examples
807    ///
808    /// ```rust,ignore
809    /// use dmsc::database::DMSCDatabaseConfig;
810    ///
811    /// let config = DMSCDatabaseConfig::postgres()
812    ///     .host("db.example.com");
813    ///
814    /// let config = DMSCDatabaseConfig::mysql()
815    ///     .host("192.168.1.100");
816    /// ```
817    pub fn host(mut self, host: &str) -> Self {
818        self.host = host.to_string();
819        self
820    }
821
822    /// Sets the database server port.
823    ///
824    /// This method configures the port number for database connections.
825    /// Each database type has a default port, but this can be overridden
826    /// for non-standard configurations or when using database proxies.
827    ///
828    /// # Arguments
829    ///
830    /// * `port` - The port number for database connections (1-65535)
831    ///
832    /// # Returns
833    ///
834    /// The updated configuration (for method chaining)
835    ///
836    /// # Common Ports
837    ///
838    /// - PostgreSQL: 5432
839    /// - MySQL: 3306
840    /// - MongoDB: 27017
841    /// - Redis: 6379
842    ///
843    /// # Examples
844    ///
845    /// ```rust,ignore
846    /// use dmsc::database::DMSCDatabaseConfig;
847    ///
848    /// // Non-standard PostgreSQL port
849    /// let config = DMSCDatabaseConfig::postgres()
850    ///     .port(15432);
851    /// ```
852    pub fn port(mut self, port: u16) -> Self {
853        self.port = port;
854        self
855    }
856
857    /// Sets the database name.
858    ///
859    /// This method configures the name of the database to connect to.
860    /// For PostgreSQL and MySQL, this is the logical database name.
861    /// For SQLite, use the `sqlite()` constructor instead.
862    ///
863    /// # Arguments
864    ///
865    /// * `database` - The name of the database to connect to
866    ///
867    /// # Returns
868    ///
869    /// The updated configuration (for method chaining)
870    ///
871    /// # Examples
872    ///
873    /// ```rust,ignore
874    /// use dmsc::database::DMSCDatabaseConfig;
875    ///
876    /// let config = DMSCDatabaseConfig::postgres()
877    ///     .database("production_db");
878    /// ```
879    pub fn database(mut self, database: &str) -> Self {
880        self.database = database.to_string();
881        self
882    }
883
884    /// Sets the database username.
885    ///
886    /// This method configures the username for database authentication.
887    /// The specified user must have sufficient privileges to perform
888    /// the required database operations.
889    ///
890    /// # Arguments
891    ///
892    /// * `user` - The username for database authentication
893    ///
894    /// # Returns
895    ///
896    /// The updated configuration (for method chaining)
897    ///
898    /// # Security Note
899    ///
900    /// For security, consider using environment variables instead of
901    /// hardcoding credentials in your code:
902    /// ```rust,ignore
903    /// use std::env;
904    ///
905    /// let config = DMSCDatabaseConfig::postgres()
906    ///     .user(&env::var("DB_USER").unwrap());
907    /// ```
908    pub fn user(mut self, user: &str) -> Self {
909        self.username = user.to_string();
910        self
911    }
912
913    /// Sets the database password.
914    ///
915    /// This method configures the password for database authentication.
916    /// The password is used together with the username to authenticate
917    /// with the database server.
918    ///
919    /// # Arguments
920    ///
921    /// * `password` - The password for database authentication
922    ///
923    /// # Returns
924    ///
925    /// The updated configuration (for method chaining)
926    ///
927    /// # Security Warning
928    ///
929    /// **Never** hardcode passwords in your source code. Use:
930    /// - Environment variables
931    /// - Secret management services (AWS Secrets Manager, HashiCorp Vault)
932    /// - Configuration files with restricted permissions
933    ///
934    /// # Examples
935    ///
936    /// ```rust,ignore
937    /// use std::env;
938    /// use dmsc::database::DMSCDatabaseConfig;
939    ///
940    /// let config = DMSCDatabaseConfig::postgres()
941    ///     .password(&env::var("DB_PASSWORD").unwrap());
942    /// ```
943    pub fn password(mut self, password: &str) -> Self {
944        self.password = password.to_string();
945        self
946    }
947
948    /// Sets the maximum number of concurrent connections.
949    ///
950    /// This method configures the upper bound of the connection pool.
951    /// The pool will not create more than this number of connections,
952    /// even under heavy load.
953    ///
954    /// # Arguments
955    ///
956    /// * `max` - Maximum number of concurrent connections (minimum 1)
957    ///
958    /// # Returns
959    ///
960    /// The updated configuration (for method chaining)
961    ///
962    /// # Performance Considerations
963    ///
964    /// - Higher values allow more concurrent database operations
965    /// - Each connection consumes memory on both client and server
966    /// - Database server has its own connection limits (e.g., PostgreSQL's `max_connections`)
967    /// - Consider using connection pooling middleware for very high concurrency
968    ///
969    /// # Recommendations
970    ///
971    /// - Development: 5-10 connections
972    /// - Production: 10-50 connections (depends on workload)
973    /// - Monitor database server connection counts
974    pub fn max_connections(mut self, max: u32) -> Self {
975        self.max_connections = max;
976        self
977    }
978
979    /// Sets the minimum number of idle connections.
980    ///
981    /// This method configures the minimum number of idle connections
982    /// that the pool will maintain. Having idle connections ready reduces
983    /// the latency of new database operations.
984    ///
985    /// # Arguments
986    ///
987    /// * `min` - Minimum number of idle connections (must be <= max_connections)
988    ///
989    /// # Returns
990    ///
991    /// The updated configuration (for method chaining)
992    ///
993    /// # Trade-offs
994    ///
995    /// - Benefits: Reduced latency for new operations
996    /// - Cost: Increased memory usage and database server load
997    ///
998    /// # Recommendations
999    ///
1000    /// - Set to expected concurrency level for best latency
1001    /// - Or use a small value (1-2) if memory is constrained
1002    pub fn min_idle_connections(mut self, min: u32) -> Self {
1003        self.min_idle_connections = min;
1004        self
1005    }
1006
1007    /// Sets the connection timeout in seconds.
1008    ///
1009    /// This method configures the maximum time to wait when establishing
1010    /// a new database connection. If a connection cannot be established
1011    /// within this time, the operation will fail with a timeout error.
1012    ///
1013    /// # Arguments
1014    ///
1015    /// * `secs` - Timeout in seconds for connection establishment
1016    ///
1017    /// # Returns
1018    ///
1019    /// The updated configuration (for method chaining)
1020    ///
1021    /// # Considerations
1022    ///
1023    /// - Too short: May cause false failures under normal load
1024    /// - Too long: May hide database server problems
1025    /// - Consider network latency to database server
1026    ///
1027    /// # Examples
1028    ///
1029    /// ```rust,ignore
1030    /// use dmsc::database::DMSCDatabaseConfig;
1031    ///
1032    /// // 60 second timeout for distant databases
1033    /// let config = DMSCDatabaseConfig::postgres()
1034    ///     .connection_timeout_secs(60);
1035    /// ```
1036    pub fn connection_timeout_secs(mut self, secs: u64) -> Self {
1037        self.connection_timeout_secs = secs;
1038        self
1039    }
1040
1041    /// Sets the idle connection timeout in seconds.
1042    ///
1043    /// This method configures how long an idle connection can exist
1044    /// before being closed. Idle connections are those that have been
1045    /// checked back into the pool but not reused.
1046    ///
1047    /// # Arguments
1048    ///
1049    /// * `secs` - Maximum idle time in seconds before connection is closed
1050    ///
1051    /// # Returns
1052    ///
1053    /// The updated configuration (for method chaining)
1054    ///
1055    /// # Purpose
1056    ///
1057    /// - Frees resources on both client and database server
1058    /// - Handles database server connection timeouts
1059    /// - Reconnects after network interruptions
1060    ///
1061    /// # Recommendations
1062    ///
1063    /// - 600 seconds (10 minutes) for typical web applications
1064    /// - 300 seconds (5 minutes) for batch processing
1065    /// - Consider database server's `wait_timeout` setting (MySQL)
1066    pub fn idle_timeout_secs(mut self, secs: u64) -> Self {
1067        self.idle_timeout_secs = secs;
1068        self
1069    }
1070
1071    /// Sets the maximum connection lifetime in seconds.
1072    ///
1073    /// This method configures the maximum age of a connection.
1074    /// Connections older than this will be closed and replaced with
1075    /// new ones when they are returned to the pool.
1076    ///
1077    /// # Arguments
1078    ///
1079    /// * `secs` - Maximum connection lifetime in seconds
1080    ///
1081    /// # Returns
1082    ///
1083    /// The updated configuration (for method chaining)
1084    ///
1085    /// # Purpose
1086    ///
1087    /// - Prevents stale connections from database server timeouts
1088    /// - Handles database server restarts gracefully
1089    /// - Rotates connections to handle network interruptions
1090    ///
1091    /// # Recommendations
1092    ///
1093    /// - 1800-3600 seconds (30-60 minutes) for most applications
1094    /// - Shorter values for very long-running applications
1095    /// - Disable by using a very large value if needed
1096    pub fn max_lifetime_secs(mut self, secs: u64) -> Self {
1097        self.max_lifetime_secs = secs;
1098        self
1099    }
1100
1101    /// Sets the SSL/TLS mode for connections.
1102    ///
1103    /// This method configures whether and how SSL/TLS encryption is used
1104    /// for database connections. For production environments, `SslMode::Require`
1105    /// is recommended to ensure all data is encrypted.
1106    ///
1107    /// # Arguments
1108    ///
1109    /// * `mode` - The SSL/TLS mode to use
1110    ///
1111    /// # Returns
1112    ///
1113    /// The updated configuration (for method chaining)
1114    ///
1115    /// # Examples
1116    ///
1117    /// ```rust,ignore
1118    /// use dmsc::database::{DMSCDatabaseConfig, SslMode};
1119    ///
1120    /// // Require SSL for production
1121    /// let config = DMSCDatabaseConfig::postgres()
1122    ///     .ssl_mode(SslMode::Require);
1123    /// ```
1124    pub fn ssl_mode(mut self, mode: SslMode) -> Self {
1125        self.ssl_mode = mode;
1126        self
1127    }
1128
1129    /// Sets the prepared statement cache size.
1130    ///
1131    /// This method configures the maximum number of prepared statements
1132    /// to cache. Prepared statements are cached to reduce the overhead
1133    /// of repeated query compilation.
1134    ///
1135    /// # Arguments
1136    ///
1137    /// * `size` - Maximum number of prepared statements to cache (0 to disable)
1138    ///
1139    /// # Returns
1140    ///
1141    /// The updated configuration (for method chaining)
1142    ///
1143    /// # Performance Impact
1144    ///
1145    /// - Benefit: Reduces query compilation overhead for repeated queries
1146    /// - Cost: Increased memory usage for statement metadata
1147    /// - Trade-off: Balance between memory and CPU usage
1148    ///
1149    /// # Recommendations
1150    ///
1151    /// - 100-500 for typical applications
1152    /// - 1000+ for applications with many repeated complex queries
1153    /// - 0 to disable statement caching (for debugging)
1154    pub fn statement_cache_size(mut self, size: u32) -> Self {
1155        self.statement_cache_size = size;
1156        self
1157    }
1158
1159    /// Builds the final configuration.
1160    ///
1161    /// This method finalizes the configuration and returns the complete
1162    /// `DMSCDatabaseConfig` instance. It is the terminal method in the
1163    /// builder chain.
1164    ///
1165    /// # Returns
1166    ///
1167    /// The complete configuration ready for use with `DMSCDatabaseManager`
1168    ///
1169    /// # Examples
1170    ///
1171    /// ```rust,ignore
1172    /// use dmsc::database::DMSCDatabaseConfig;
1173    ///
1174    /// let config = DMSCDatabaseConfig::postgres()
1175    ///     .host("localhost")
1176    ///     .database("myapp")
1177    ///     .user("app")
1178    ///     .password("secret")
1179    ///     .max_connections(10)
1180    ///     .build();
1181    /// ```
1182    pub fn build(self) -> DMSCDatabaseConfig {
1183        self
1184    }
1185
1186    /// Generates a connection string for the configured database.
1187    ///
1188    /// This method creates a database-specific connection string URI
1189    /// that can be used with various database client libraries.
1190    ///
1191    /// # Returns
1192    ///
1193    /// A String containing the connection string
1194    ///
1195    /// # Examples
1196    ///
1197    /// ```rust,ignore
1198    /// use dmsc::database::DMSCDatabaseConfig;
1199    ///
1200    /// let config = DMSCDatabaseConfig::postgres()
1201    ///     .host("localhost")
1202    ///     .port(5432)
1203    ///     .database("myapp")
1204    ///     .user("app")
1205    ///     .password("secret");
1206    ///
1207    /// let connection_string = config.connection_string();
1208    /// // postgresql://app:secret@localhost:5432/myapp
1209    /// ```
1210    pub fn connection_string(&self) -> String {
1211        match self.database_type {
1212            DatabaseType::Postgres => {
1213                format!(
1214                    "postgresql://{}:{}@{}:{}/{}",
1215                    self.username, self.password, self.host, self.port, self.database
1216                )
1217            }
1218            DatabaseType::MySQL => {
1219                format!(
1220                    "mysql://{}:{}@{}:{}/{}",
1221                    self.username, self.password, self.host, self.port, self.database
1222                )
1223            }
1224            DatabaseType::SQLite => self.database.clone(),
1225            DatabaseType::MongoDB => {
1226                format!(
1227                    "mongodb://{}:{}@{}:{}/{}",
1228                    self.username, self.password, self.host, self.port, self.database
1229                )
1230            }
1231            DatabaseType::Redis => {
1232                format!(
1233                    "redis://{}:{}@{}:{}",
1234                    self.username, self.password, self.host, self.port
1235                )
1236            }
1237        }
1238    }
1239}
1240
1241impl Default for DMSCDatabaseConfig {
1242    fn default() -> Self {
1243        Self::postgres()
1244    }
1245}