database_replicator/
lib.rs

1// ABOUTME: Library module for database-replicator
2// ABOUTME: Exports all core functionality for use in binary and tests
3
4pub mod checkpoint;
5pub mod commands;
6pub mod config;
7pub mod filters;
8pub mod interactive;
9pub mod jsonb;
10pub mod migration;
11pub mod mongodb;
12pub mod mysql;
13pub mod postgres;
14pub mod preflight;
15pub mod remote;
16pub mod replication;
17pub mod serendb;
18pub mod sqlite;
19pub mod state;
20pub mod table_rules;
21pub mod utils;
22
23use anyhow::{bail, Result};
24
25/// Database source types supported for replication
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum SourceType {
28    /// PostgreSQL database (postgresql:// or postgres:// URL)
29    PostgreSQL,
30    /// SQLite database file (.db, .sqlite, .sqlite3)
31    SQLite,
32    /// MongoDB database (mongodb:// or mongodb+srv:// URL)
33    MongoDB,
34    /// MySQL database (mysql:// URL)
35    MySQL,
36}
37
38/// Detect the source database type from connection string or file path
39///
40/// Detection rules:
41/// - PostgreSQL: Starts with `postgresql://` or `postgres://`
42/// - SQLite: Ends with `.db`, `.sqlite`, or `.sqlite3`
43/// - MongoDB: Starts with `mongodb://` or `mongodb+srv://`
44/// - MySQL: Starts with `mysql://`
45///
46/// # Arguments
47///
48/// * `source` - Database connection string or file path
49///
50/// # Returns
51///
52/// Detected source type or error if type cannot be determined
53///
54/// # Examples
55///
56/// ```
57/// # use database_replicator::{detect_source_type, SourceType};
58/// assert_eq!(detect_source_type("postgresql://localhost/db").unwrap(), SourceType::PostgreSQL);
59/// assert_eq!(detect_source_type("database.db").unwrap(), SourceType::SQLite);
60/// assert_eq!(detect_source_type("data.sqlite3").unwrap(), SourceType::SQLite);
61/// assert!(detect_source_type("invalid").is_err());
62/// ```
63pub fn detect_source_type(source: &str) -> Result<SourceType> {
64    if source.starts_with("postgresql://") || source.starts_with("postgres://") {
65        Ok(SourceType::PostgreSQL)
66    } else if source.starts_with("mongodb://") || source.starts_with("mongodb+srv://") {
67        Ok(SourceType::MongoDB)
68    } else if source.starts_with("mysql://") {
69        Ok(SourceType::MySQL)
70    } else if source.ends_with(".db") || source.ends_with(".sqlite") || source.ends_with(".sqlite3")
71    {
72        Ok(SourceType::SQLite)
73    } else {
74        bail!(
75            "Could not detect source database type from '{}'.\n\
76             Supported sources:\n\
77             - PostgreSQL: postgresql://... or postgres://...\n\
78             - SQLite: path ending with .db, .sqlite, or .sqlite3\n\
79             - MongoDB: mongodb://... or mongodb+srv://...\n\
80             - MySQL: mysql://...",
81            source
82        )
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_detect_postgresql() {
92        assert_eq!(
93            detect_source_type("postgresql://localhost/mydb").unwrap(),
94            SourceType::PostgreSQL
95        );
96        assert_eq!(
97            detect_source_type("postgres://user:pass@host:5432/db").unwrap(),
98            SourceType::PostgreSQL
99        );
100    }
101
102    #[test]
103    fn test_detect_sqlite() {
104        assert_eq!(
105            detect_source_type("database.db").unwrap(),
106            SourceType::SQLite
107        );
108        assert_eq!(
109            detect_source_type("data.sqlite").unwrap(),
110            SourceType::SQLite
111        );
112        assert_eq!(
113            detect_source_type("/path/to/database.sqlite3").unwrap(),
114            SourceType::SQLite
115        );
116    }
117
118    #[test]
119    fn test_detect_mongodb() {
120        assert_eq!(
121            detect_source_type("mongodb://localhost/db").unwrap(),
122            SourceType::MongoDB
123        );
124        assert_eq!(
125            detect_source_type("mongodb+srv://cluster.mongodb.net/db").unwrap(),
126            SourceType::MongoDB
127        );
128    }
129
130    #[test]
131    fn test_detect_mysql() {
132        assert_eq!(
133            detect_source_type("mysql://localhost/db").unwrap(),
134            SourceType::MySQL
135        );
136        assert_eq!(
137            detect_source_type("mysql://user:pass@host:3306/db").unwrap(),
138            SourceType::MySQL
139        );
140    }
141
142    #[test]
143    fn test_detect_invalid_source() {
144        let result = detect_source_type("invalid_source");
145        assert!(result.is_err());
146        assert!(result
147            .unwrap_err()
148            .to_string()
149            .contains("Could not detect source database type"));
150    }
151
152    #[test]
153    fn test_detect_empty_string() {
154        let result = detect_source_type("");
155        assert!(result.is_err());
156    }
157}