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