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}