Skip to content

Commit 96012b8

Browse files
committed
feat: add export_handlers module and enhance import/export functionality with path handling
1 parent 08ecc48 commit 96012b8

File tree

7 files changed

+207
-137
lines changed

7 files changed

+207
-137
lines changed

src/dump_sync.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl DumpSync {
3838
.expect("Invalid port");
3939

4040
UI::section_header("Importing dump to server", "info");
41-
Dump::new(&host, port, &user, &password, &dbname, &backup_path, None).import();
41+
Dump::new(&host, port, &user, &password, &dbname, &backup_path, None, &backup_path).import();
4242
}
4343

4444
fn export(&self, options: ExportOptions) {
@@ -67,7 +67,7 @@ impl DumpSync {
6767

6868
UI::label("Press CTRL+C to exit the tool", "normal");
6969
UI::section_header("Dumping the database", "info");
70-
Dump::new(&host, port, &user, &password, &dbname, &backup_path, Some(interval)).export();
70+
Dump::new(&host, port, &user, &password, &dbname, &backup_path, Some(interval), &backup_path).export();
7171
}
7272

7373
pub fn init(&self) {

src/engine/dump.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use std::{
2-
thread,
3-
process,
2+
thread,
3+
process,
44
time::Duration,
5-
5+
66
sync::{
77
Arc,
88

99
atomic::{
10-
Ordering,
1110
AtomicBool,
1211
AtomicUsize,
13-
},
14-
},
12+
Ordering
13+
}
14+
},
1515
};
1616

1717
use crate::{
@@ -30,6 +30,7 @@ use crate::{
3030

3131
pub struct Dump {
3232
port: u64,
33+
path: String,
3334
host: String,
3435
user: String,
3536
interval: u64,
@@ -50,15 +51,17 @@ impl Dump {
5051
dbname: &str,
5152
backup_path: &str,
5253
interval: Option<u64>,
54+
path: &str,
5355
) -> Self {
5456
Self {
55-
host: host.to_string(),
5657
port,
58+
host: host.to_string(),
5759
user: user.to_string(),
58-
password: password.to_string(),
5960
dbname: dbname.to_string(),
60-
interval: interval.unwrap_or(3600),
61+
password: password.to_string(),
6162
dump_file_path: backup_path.to_string(),
63+
interval: interval.unwrap_or(3600),
64+
path: path.to_string(),
6265
}
6366
}
6467

@@ -102,6 +105,7 @@ impl Dump {
102105
let password_clone = self.password.clone();
103106
let dbname_clone = self.dbname.clone();
104107
let interval_clone = self.interval;
108+
let path_clone = self.path.clone();
105109

106110
ctrlc::set_handler(move || {
107111
running.store(false, Ordering::SeqCst);
@@ -114,6 +118,7 @@ impl Dump {
114118
dbname: dbname_clone.clone(),
115119
interval: interval_clone,
116120
dump_file_path: dump_file_path_clone.clone(),
121+
path: path_clone.clone(),
117122
};
118123

119124
let dump_count = DUMP_COUNT.load(Ordering::SeqCst);
@@ -136,6 +141,7 @@ impl Dump {
136141
&self.password,
137142
&self.dbname,
138143
&self.dump_file_path,
144+
&self.path,
139145
).dump().expect("Failed to import dump");
140146
}
141147

src/engine/export.rs

Lines changed: 23 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,24 @@
11
use std::{
22
fs::File,
33
error::Error,
4-
5-
io::{
6-
self,
7-
Write,
8-
BufWriter
9-
},
10-
};
11-
12-
use flate2::{
13-
Compression,
14-
write::GzEncoder
154
};
165

176
use mysql::{
187
*,
19-
Row,
208
prelude::*
219
};
2210

2311
use crate::{
24-
ui::success_alerts::SuccessAlerts,
25-
26-
utils::{
27-
date::Date,
28-
file::FileUtils
29-
},
12+
utils::file::FileUtils,
13+
ui::success_alerts::SuccessAlerts,
14+
helpers::export_handlers::ExportHandlers,
3015

3116
engine::{
3217
configs::Configs,
3318
connection::Connection
3419
},
3520
};
3621

37-
enum Writer {
38-
Compressed(BufWriter<GzEncoder<File>>),
39-
Uncompressed(BufWriter<File>),
40-
}
41-
42-
impl Writer {
43-
fn as_write(&mut self) -> &mut dyn Write {
44-
match self {
45-
Writer::Compressed(w) => w,
46-
Writer::Uncompressed(w) => w,
47-
}
48-
}
49-
}
50-
5122
pub struct Export {
5223
pub host: String,
5324
pub port: u16,
@@ -58,8 +29,15 @@ pub struct Export {
5829
}
5930

6031
impl Export {
61-
62-
pub fn new(host: &str, port: u16, user: &str, password: &str, dbname: &str, dump_file_path: &str) -> Self {
32+
33+
pub fn new(
34+
host: &str,
35+
port: u16,
36+
user: &str,
37+
password: &str,
38+
dbname: &str,
39+
dump_file_path: &str
40+
) -> Self {
6341
Self {
6442
host: host.to_string(),
6543
port,
@@ -70,85 +48,6 @@ impl Export {
7048
}
7149
}
7250

73-
fn create_writer(&self, file: File, compress_data: bool) -> Writer {
74-
if compress_data {
75-
let encoder = GzEncoder::new(file, Compression::default());
76-
Writer::Compressed(BufWriter::new(encoder))
77-
} else {
78-
Writer::Uncompressed(BufWriter::new(file))
79-
}
80-
}
81-
82-
fn comments_header(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
83-
writeln!(writer, "-- Exporting using {} v.{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))?;
84-
writeln!(writer, "-- Database backup: {}", self.dbname)?;
85-
writeln!(writer, "-- Export date and time: {}", Date::timestamp())?;
86-
writeln!(writer, "-- ---------------------------------------------------\n")?;
87-
88-
Ok(())
89-
}
90-
91-
fn write_create_new_database(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
92-
let database_if_not_exists = Configs.boolean("exports", "database_if_not_exists", true);
93-
94-
if database_if_not_exists {
95-
writeln!(writer, "CREATE DATABASE IF NOT EXISTS `{}`;", self.dbname)?;
96-
writeln!(writer, "USE `{}`;", self.dbname)?;
97-
writeln!(writer, "-- ---------------------------------------------------\n")?;
98-
}
99-
Ok(())
100-
}
101-
102-
fn write_inserts_for_table(&self, table: &str, conn: &mut PooledConn, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
103-
let dump_data = Configs.boolean("exports", "dump_data", true);
104-
let insert_ignore_into = Configs.boolean("exports", "insert_ignore_into", false);
105-
106-
if dump_data {
107-
let rows: Vec<Row> = conn.query(format!("SELECT * FROM `{}`", table))?;
108-
109-
if rows.is_empty() {
110-
writeln!(writer, "-- Table `{}` contains no data.", table)?;
111-
} else {
112-
for row in rows {
113-
let values: Vec<String> = row.clone().unwrap().into_iter().map(|value| match value {
114-
Value::NULL => "NULL".to_string(),
115-
Value::Bytes(bytes) => format!("'{}'", String::from_utf8_lossy(&bytes)),
116-
Value::Int(int) => int.to_string(),
117-
Value::UInt(uint) => uint.to_string(),
118-
Value::Float(float) => float.to_string(),
119-
_ => "NULL".to_string(),
120-
}).collect();
121-
122-
let line = if insert_ignore_into {
123-
format!("INSERT IGNORE INTO `{}` VALUES ({});", table, values.join(", "))
124-
} else {
125-
format!("INSERT INTO `{}` VALUES ({});", table, values.join(", "))
126-
};
127-
128-
writeln!(writer, "{}", line)?;
129-
}
130-
}
131-
}
132-
133-
Ok(())
134-
}
135-
136-
fn write_structure_for_table(&self, table: &str, conn: &mut PooledConn, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
137-
let drop_table_if_exists = Configs.boolean("exports", "drop_table_if_exists", false);
138-
139-
writeln!(writer, "-- Exporting the table: `{}`", table)?;
140-
141-
if drop_table_if_exists {
142-
writeln!(writer, "DROP TABLE IF EXISTS `{}`;", table)?;
143-
}
144-
145-
let row: Row = conn.query_first(format!("SHOW CREATE TABLE `{}`", table))?.unwrap();
146-
let create_table: String = row.get(1).expect("Error retrieving CREATE TABLE");
147-
writeln!(writer, "{};\n", create_table)?;
148-
149-
Ok(())
150-
}
151-
15251
pub fn dump(&self) -> Result<(), Box<dyn Error>> {
15352
let compress_data = Configs.boolean("exports", "compress_data", false);
15453

@@ -158,6 +57,11 @@ impl Export {
15857
self.dump_file_path.clone()
15958
};
16059

60+
let export_handlers = ExportHandlers::new(
61+
File::create(dump_file_path.clone())?,
62+
&self.dbname
63+
);
64+
16165
let pool = Connection {
16266
host: self.host.clone(),
16367
port: self.port,
@@ -166,14 +70,13 @@ impl Export {
16670
dbname: Some(self.dbname.clone()),
16771
}.create_pool()?;
16872

169-
FileUtils::create_path(&dump_file_path);
73+
FileUtils::create_path(&dump_file_path.clone());
17074

17175
let mut conn = pool.get_conn()?;
172-
let file = File::create(&dump_file_path)?;
173-
let mut writer = self.create_writer(file, compress_data);
76+
let mut writer = export_handlers.create_writer()?;
17477

175-
self.comments_header(writer.as_write())?;
176-
self.write_create_new_database(writer.as_write())?;
78+
export_handlers.comments_header(writer.as_write())?;
79+
export_handlers.write_create_new_database(writer.as_write())?;
17780

17881
let tables: Vec<String> = conn.query("SHOW TABLES")?;
17982
let ignore_tables = Configs.list("exports", "ignore_tables").unwrap_or_default();
@@ -184,13 +87,12 @@ impl Export {
18487
continue;
18588
}
18689

187-
self.write_structure_for_table(&table, &mut conn, writer.as_write())?;
188-
self.write_inserts_for_table(&table, &mut conn, writer.as_write())?;
90+
export_handlers.write_structure_for_table(&table, &mut conn, writer.as_write())?;
91+
export_handlers.write_inserts_for_table(&table, &mut conn, writer.as_write())?;
18992
writeln!(writer.as_write(), "-- End of table `{}`", table)?;
19093
}
19194

19295
SuccessAlerts::dump(&dump_file_path);
193-
io::stdout().flush().unwrap();
19496

19597
Ok(())
19698
}

src/engine/import.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ use std::{
1010
fs::File,
1111
error::Error,
1212

13+
path::{
14+
Path,
15+
PathBuf
16+
},
17+
1318
io::{
1419
Read,
1520
BufReader,
@@ -31,22 +36,35 @@ pub struct Import {
3136
user: String,
3237
password: String,
3338
dbname: String,
39+
path: String,
3440
dump_file_path: String,
3541
}
3642

3743
impl Import {
3844

39-
pub fn new(host: &str, port: u16, user: &str, password: &str, dbname: &str, dump_file_path: &str) -> Self {
45+
pub fn new(host: &str, port: u16, user: &str, password: &str, dbname: &str, dump_file_path: &str, path: &str) -> Self {
4046
Self {
4147
host: host.to_string(),
4248
port,
4349
user: user.to_string(),
4450
password: password.to_string(),
4551
dbname: dbname.to_string(),
52+
path: path.to_string(),
4653
dump_file_path: dump_file_path.to_string(),
4754
}
4855
}
4956

57+
fn complete_path(&self) -> Result<PathBuf, Box<dyn std::error::Error>> {
58+
let path = Path::new(&self.dump_file_path);
59+
60+
if path.is_absolute() {
61+
Ok(path.to_path_buf())
62+
} else {
63+
let dump_file_path = Path::new(&self.dump_file_path);
64+
Ok(dump_file_path.join(&self.path))
65+
}
66+
}
67+
5068
pub fn dump(&self) -> Result<(), Box<dyn Error>> {
5169
let pool = Connection {
5270
host: self.host.clone(),
@@ -59,8 +77,10 @@ impl Import {
5977
let mut conn = pool.get_conn()?;
6078
let is_compressed = self.dump_file_path.ends_with(".sql.gz");
6179

80+
let file = self.complete_path()?;
81+
6282
let dump_content = if is_compressed {
63-
let file = File::open(&self.dump_file_path)?;
83+
let file = File::open(file)?;
6484

6585
let mut decoder = GzDecoder::new(BufReader::new(file));
6686
let mut content = String::new();
@@ -79,7 +99,7 @@ impl Import {
7999

80100
for statement in dump_content.split(';') {
81101
let trimmed = statement.trim();
82-
102+
83103
if !trimmed.is_empty() {
84104
match conn.query_drop(trimmed) {
85105
Ok(_) => {

0 commit comments

Comments
 (0)