1use std::collections::HashMap;
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum DatabaseType {
11 SQLite,
12 Postgres,
13}
14
15#[derive(Debug)]
17pub struct Connection {
18 connection_string: String,
19 db_type: DatabaseType,
20}
21
22#[derive(Debug, Clone)]
24pub struct Row {
25 pub columns: HashMap<String, String>,
26}
27
28impl Row {
29 pub fn get(&self, column: &str) -> Option<String> {
31 self.columns.get(column).cloned()
32 }
33
34 pub fn get_int(&self, column: &str) -> Option<i64> {
36 self.get(column)?.parse().ok()
37 }
38
39 pub fn get_float(&self, column: &str) -> Option<f64> {
41 self.get(column)?.parse().ok()
42 }
43
44 pub fn get_bool(&self, column: &str) -> Option<bool> {
46 self.get(column)?.parse().ok()
47 }
48}
49
50pub fn open_sqlite(path: &str) -> Result<Connection, String> {
52 Ok(Connection {
53 connection_string: path.to_string(),
54 db_type: DatabaseType::SQLite,
55 })
56}
57
58pub fn open_postgres(connection_string: &str) -> Result<Connection, String> {
61 if !connection_string.starts_with("postgres://")
62 && !connection_string.starts_with("postgresql://")
63 {
64 return Err(
65 "PostgreSQL connection string must start with postgres:// or postgresql://".to_string(),
66 );
67 }
68
69 Ok(Connection {
70 connection_string: connection_string.to_string(),
71 db_type: DatabaseType::Postgres,
72 })
73}
74
75pub fn open(connection_string: &str) -> Result<Connection, String> {
77 if connection_string.starts_with("postgres://")
78 || connection_string.starts_with("postgresql://")
79 {
80 open_postgres(connection_string)
81 } else {
82 open_sqlite(connection_string)
83 }
84}
85
86impl Connection {
87 pub fn db_type(&self) -> &DatabaseType {
89 &self.db_type
90 }
91
92 pub fn connection_string(&self) -> &str {
94 &self.connection_string
95 }
96
97 pub fn query(&self, sql: &str, _params: &[String]) -> Result<Vec<Row>, String> {
99 Err(format!(
102 "Database query not yet fully implemented. SQL: {} (type: {:?})",
103 sql, self.db_type
104 ))
105 }
106
107 pub fn execute(&self, sql: &str, _params: &[String]) -> Result<u64, String> {
109 Err(format!(
112 "Database execute not yet fully implemented. SQL: {} (type: {:?})",
113 sql, self.db_type
114 ))
115 }
116
117 pub fn begin_transaction(&self) -> Result<(), String> {
119 Err("Transactions not yet implemented".to_string())
120 }
121
122 pub fn commit(&self) -> Result<(), String> {
124 Err("Transactions not yet implemented".to_string())
125 }
126
127 pub fn rollback(&self) -> Result<(), String> {
129 Err("Transactions not yet implemented".to_string())
130 }
131
132 pub fn close(self) -> Result<(), String> {
134 Ok(())
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_open_sqlite() {
144 let conn = open_sqlite(":memory:");
145 assert!(conn.is_ok());
146 let conn = conn.unwrap();
147 assert_eq!(conn.db_type(), &DatabaseType::SQLite);
148 }
149
150 #[test]
151 fn test_open_postgres() {
152 let conn = open_postgres("postgres://user:pass@localhost/db");
153 assert!(conn.is_ok());
154 let conn = conn.unwrap();
155 assert_eq!(conn.db_type(), &DatabaseType::Postgres);
156 }
157
158 #[test]
159 fn test_open_postgres_invalid() {
160 let conn = open_postgres("invalid://connection");
161 assert!(conn.is_err());
162 }
163
164 #[test]
165 fn test_open_auto_detect() {
166 let sqlite_conn = open(":memory:").unwrap();
168 assert_eq!(sqlite_conn.db_type(), &DatabaseType::SQLite);
169
170 let pg_conn = open("postgres://localhost/db").unwrap();
172 assert_eq!(pg_conn.db_type(), &DatabaseType::Postgres);
173
174 let pg_conn2 = open("postgresql://localhost/db").unwrap();
176 assert_eq!(pg_conn2.db_type(), &DatabaseType::Postgres);
177 }
178
179 #[test]
180 fn test_row_get() {
181 let mut columns = HashMap::new();
182 columns.insert("name".to_string(), "Alice".to_string());
183 columns.insert("age".to_string(), "30".to_string());
184 columns.insert("active".to_string(), "true".to_string());
185 columns.insert("score".to_string(), "95.5".to_string());
186
187 let row = Row { columns };
188 assert_eq!(row.get("name"), Some("Alice".to_string()));
189 assert_eq!(row.get_int("age"), Some(30));
190 assert_eq!(row.get_bool("active"), Some(true));
191 assert_eq!(row.get_float("score"), Some(95.5));
192 }
193
194 #[test]
195 fn test_query_placeholder() {
196 let conn = open(":memory:").unwrap();
197 let result = conn.query("SELECT * FROM users", &[]);
198 assert!(result.is_err());
200 }
201
202 #[test]
203 fn test_execute_placeholder() {
204 let conn = open_postgres("postgres://localhost/test").unwrap();
205 let result = conn.execute("INSERT INTO users (name) VALUES ('Alice')", &[]);
206 assert!(result.is_err());
208 }
209}