etanol_databases/
structs.rs1use std::fs::File;
2use std::path::PathBuf;
3use std::slice::Iter;
4use std::sync::{Arc, Mutex};
5
6use etanol_utils::EtanolError;
7use rusqlite::{types::ValueRef, Connection, Error};
8
9pub use rusqlite::{params_from_iter, Params, ParamsFromIter};
10
11use crate::Column;
12
13lazy_static::lazy_static! {
14 static ref SQLITE_CONNECTION: Arc<Mutex<Option<Connection>>> = Arc::new(Mutex::new(None));
15}
16
17pub fn createParams(params: &[String]) -> ParamsFromIter<Iter<String>> {
18 params_from_iter(params.iter())
19}
20
21#[derive(Debug)]
22pub struct Database {}
23
24impl Database {
25 fn connectionError() {
26 EtanolError::new(
27 format!(
28 "Connection to database is not initialized. Please call `create_connection` first."
29 ),
30 "DatabaseConnection".to_string(),
31 );
32 }
33
34 pub fn createTable(name: String, columns: Vec<Column>) -> String {
35 let mut sql = String::from("--Create Table\n");
36
37 sql.push_str(&format!("CREATE TABLE IF NOT EXISTS \"{}\" (\n", name));
38
39 for column in columns {
40 sql.push_str(&format!(
41 " \"{}\" {}",
42 column.name,
43 Database::databaseType(column.columnType)
44 ));
45
46 if !column.isOptional {
47 sql.push_str(" NOT NULL");
48 }
49
50 if column.isPrimary {
51 sql.push_str(" PRIMARY KEY");
52 }
53
54 sql.push_str(",\n");
55 }
56
57 sql.remove(sql.len() - 2);
58 sql.push_str(");\n");
59
60 sql
61 }
62
63 pub fn createConnection(url: String) -> Result<(), Error> {
64 if !url.ends_with(".sqlite") {
65 EtanolError::new(
66 format!("Config database_url '{}' not supported", url),
67 "DatabaseNotSupported".to_string(),
68 );
69 }
70
71 let url = format!("etanol/{}", url);
72 let path = PathBuf::from(url.clone());
73 if !path.exists() {
74 match File::create(path) {
75 Ok(_) => {}
76 Err(e) => {
77 EtanolError::new(
78 format!("Could not create database file: {}", e),
79 "FileSystemError".to_string(),
80 );
81 }
82 }
83 }
84
85 if SQLITE_CONNECTION.lock().unwrap().is_none() {
86 if let Ok(conn) = Connection::open(url) {
87 *SQLITE_CONNECTION.lock().unwrap() = Some(conn);
88
89 return Ok(());
90 }
91
92 EtanolError::new(
93 format!("Could not create database connection"),
94 "DatabaseConnection".to_string(),
95 );
96 }
97
98 Ok(())
99 }
100
101 pub fn executeWithResults(
102 sql: String,
103 params: &[String],
104 ) -> Result<Vec<Vec<(String, String)>>, Error> {
105 if let Some(conn) = &*SQLITE_CONNECTION.lock().unwrap() {
106 let mut stmt = conn.prepare(&sql).unwrap();
107
108 let names = stmt
109 .column_names()
110 .into_iter()
111 .map(|s| String::from(s))
112 .collect::<Vec<_>>();
113
114 let mut rows = stmt.query(createParams(params)).unwrap();
115 let mut models = Vec::new();
116
117 while let Ok(row) = rows.next() {
118 if let Some(row) = row {
119 let mut model = vec![];
120
121 for name in names.iter() {
122 let value = match row.get_ref_unwrap(name.as_ref()) {
123 ValueRef::Integer(i) => i.to_string(),
124 ValueRef::Text(s) => String::from_utf8(s.to_vec()).unwrap(),
125 _ => "None".to_string(),
126 };
127
128 model.push((name.clone(), value));
129 }
130
131 models.push(model);
132 } else {
133 break;
134 }
135 }
136
137 return Ok(models);
138 }
139
140 Database::connectionError();
141
142 Ok(vec![])
143 }
144
145 pub fn execute(sql: String, params: &[String]) -> Result<(), Error> {
146 if let Some(conn) = &*SQLITE_CONNECTION.lock().unwrap() {
147 conn.execute(&sql, params_from_iter(params.iter())).unwrap();
148
149 return Ok(());
150 }
151
152 Database::connectionError();
153
154 Ok(())
155 }
156
157 pub fn databaseType(columnType: String) -> String {
158 match columnType.as_str() {
159 "Integer" => String::from("INTEGER"),
160 "String" => String::from("TEXT"),
161 "Boolean" => String::from("BOOLEAN"),
162 _ => {
163 panic!("Invalid column type: {}", columnType);
164 }
165 }
166 }
167
168 pub fn formatQuery(query: String) -> String {
169 query
170 .split("\n")
171 .filter(|x| !x.to_string().starts_with("--"))
172 .collect::<Vec<&str>>()
173 .join("")
174 .split("\"")
175 .collect::<Vec<&str>>()
176 .join("")
177 .to_string()
178 }
179}
180
181