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