Skip to content
This repository was archived by the owner on Apr 16, 2023. It is now read-only.

Commit 3c5ba93

Browse files
committed
* Added ability to export to JSON, CSV and TXT
1 parent 9f1e7d7 commit 3c5ba93

File tree

6 files changed

+244
-9
lines changed

6 files changed

+244
-9
lines changed

Cargo.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ license-file = "LICENSE"
1212
[dependencies]
1313
iced = { git = "https://github.com/iced-rs/iced" }
1414
native-dialog = "0.6.3"
15+
serde = { version = "1.0", features = ["derive"] }
16+
serde_json = "1.0"
1517

1618
[profile.release]
1719
lto = true

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ cargo run
4040
* [Rust](https://www.rust-lang.org/)
4141
* [iced](https://iced.rs/)
4242
* [native-dialog](https://github.com/balthild/native-dialog-rs)
43+
* [serde](https://serde.rs/)
44+
* [serde_json](https://serde.rs/)
4345

4446
## About
4547

src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use iced::window;
22
use iced::{Sandbox, Settings};
33

4-
mod view;
5-
mod style;
64
mod filereader;
5+
mod style;
76
mod vector_comparer;
7+
mod vector_exporter;
8+
mod view;
89

910
pub fn main() -> iced::Result {
1011
view::ApplicationContext::run(Settings {

src/vector_exporter.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use std::{fs::File, io::Write};
2+
3+
#[derive(Debug, Clone)]
4+
pub enum ExportType {
5+
Text,
6+
Csv,
7+
Json,
8+
}
9+
10+
pub enum ExportError {
11+
IoError(std::io::Error),
12+
JsonError(serde_json::Error),
13+
}
14+
15+
impl Default for ExportType {
16+
fn default() -> Self {
17+
ExportType::Text
18+
}
19+
}
20+
21+
pub trait IVectorExporter<T> {
22+
fn new(vec: Vec<T>, export_type: ExportType, export_type: &str) -> Self;
23+
24+
fn export(&self) -> Result<(), ExportError>;
25+
}
26+
27+
#[derive(Debug, Clone)]
28+
pub struct VectorExporter<T> {
29+
pub vec: Vec<T>,
30+
pub export_type: ExportType,
31+
pub export_path: String,
32+
}
33+
34+
impl IVectorExporter<String> for VectorExporter<String> {
35+
/// Initialize a new `VectorExporter` for type `String`
36+
///
37+
/// # Example
38+
///
39+
/// ```rust
40+
/// let vec_exporter: VectorExporter<String> = IVectorExporter::<String>::new(vec![], ExportType::default(), "/path/to/file");
41+
/// ```
42+
///
43+
/// # Returns
44+
///
45+
/// The `VectorExporter` struct for type `String`
46+
fn new(vec: Vec<String>, export_type: ExportType, export_path: &str) -> VectorExporter<String> {
47+
VectorExporter {
48+
vec,
49+
export_type,
50+
export_path: String::from(export_path),
51+
}
52+
}
53+
54+
/// Export the `Vec` of type `String` to a file
55+
///
56+
/// # Example
57+
///
58+
/// ```rust
59+
/// let res = vec_exporter.export();
60+
/// ```
61+
///
62+
/// # Returns
63+
///
64+
/// A `Result` that can either contain an `Ok` or an `Error` struct
65+
fn export(&self) -> Result<(), ExportError> {
66+
let file = File::create(&self.export_path);
67+
let mut file = match file {
68+
Ok(file) => file,
69+
Err(e) => return Err(ExportError::IoError(e)),
70+
};
71+
match self.export_type {
72+
ExportType::Text => {
73+
let mut data = String::new();
74+
for l in &self.vec {
75+
data.push_str(&format!("{}\n", l));
76+
}
77+
78+
match write!(file, "{}", data) {
79+
Ok(_) => Ok(()),
80+
Err(e) => Err(ExportError::IoError(e)),
81+
}
82+
}
83+
ExportType::Csv => {
84+
let mut data = String::new();
85+
for l in &self.vec {
86+
data.push_str(&format!("\"{}\"\n", l));
87+
}
88+
89+
match write!(file, "{}", data) {
90+
Ok(_) => Ok(()),
91+
Err(e) => Err(ExportError::IoError(e)),
92+
}
93+
}
94+
ExportType::Json => {
95+
let serialized = match serde_json::to_string(&self.vec) {
96+
Ok(d) => d,
97+
Err(e) => return Err(ExportError::JsonError(e)),
98+
};
99+
100+
match write!(file, "{}", serialized) {
101+
Ok(_) => Ok(()),
102+
Err(e) => Err(ExportError::IoError(e)),
103+
}
104+
}
105+
}
106+
}
107+
}

src/view.rs

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
use std::ffi::OsStr;
2+
use std::path::Path;
3+
14
use crate::filereader::FileReader;
25
use crate::style;
36
use crate::vector_comparer::{IVectorComparer, VectorComparer};
7+
use crate::vector_exporter::{ExportType, IVectorExporter, VectorExporter};
48
use iced::{alignment, scrollable, Rule};
59
use iced::{
610
button, text_input, Alignment, Button, Column, Container, Element, Length, Radio, Row, Sandbox,
@@ -17,6 +21,7 @@ pub enum Message {
1721
SelectSecondFilePressed,
1822
ComparePressed,
1923
ClearComparePressed,
24+
ExportPressed,
2025
}
2126

2227
#[derive(Default)]
@@ -30,6 +35,7 @@ pub struct ApplicationContext {
3035
pub btn_select_second_file: button::State,
3136
pub btn_compare: button::State,
3237
pub btn_clean_compare: button::State,
38+
pub btn_export: button::State,
3339
pub scrollable: scrollable::State,
3440
pub differences: Vec<String>,
3541
pub has_compared: bool,
@@ -100,7 +106,10 @@ impl Sandbox for ApplicationContext {
100106
MessageDialog::new()
101107
.set_type(MessageType::Error)
102108
.set_title("text-diff")
103-
.set_text(&format!("Error while reading file {}!\n{}", &self.first_file, e))
109+
.set_text(&format!(
110+
"Error while reading file {}!\n{}",
111+
&self.first_file, e
112+
))
104113
.show_alert()
105114
.unwrap();
106115
return;
@@ -113,7 +122,10 @@ impl Sandbox for ApplicationContext {
113122
MessageDialog::new()
114123
.set_type(MessageType::Error)
115124
.set_title("text-diff")
116-
.set_text(&format!("Error while reading file {}!\n{}", &self.second_file, e))
125+
.set_text(&format!(
126+
"Error while reading file {}!\n{}",
127+
&self.second_file, e
128+
))
117129
.show_alert()
118130
.unwrap();
119131
return;
@@ -133,6 +145,61 @@ impl Sandbox for ApplicationContext {
133145
self.has_compared = false;
134146
self.differences = vec![];
135147
}
148+
Message::ExportPressed => {
149+
let path = FileDialog::new()
150+
.add_filter("Text file", &["txt"])
151+
.add_filter("Csv file", &["csv"])
152+
.add_filter("Json file", &["json"])
153+
.show_save_single_file()
154+
.unwrap();
155+
156+
let path = match path {
157+
Some(path) => path,
158+
None => return,
159+
};
160+
161+
let path = path.into_os_string().into_string().unwrap();
162+
163+
let extension = match Path::new(&path).extension().and_then(OsStr::to_str) {
164+
Some(x) => x,
165+
None => return,
166+
};
167+
168+
let extension = match extension.to_lowercase().as_str() {
169+
"txt" => ExportType::Text,
170+
"csv" => ExportType::Csv,
171+
"json" => ExportType::Json,
172+
_ => ExportType::default(),
173+
};
174+
175+
let vec_exporter: VectorExporter<String> =
176+
IVectorExporter::<String>::new(self.differences.clone(), extension, &path);
177+
178+
match vec_exporter.export() {
179+
Ok(_) => return,
180+
Err(e) => match e {
181+
crate::vector_exporter::ExportError::IoError(e) => {
182+
MessageDialog::new()
183+
.set_type(MessageType::Error)
184+
.set_title("text-diff")
185+
.set_text(&format!("Error while writing to file {}!\n{}", &path, e))
186+
.show_alert()
187+
.unwrap();
188+
}
189+
crate::vector_exporter::ExportError::JsonError(e) => {
190+
MessageDialog::new()
191+
.set_type(MessageType::Error)
192+
.set_title("text-diff")
193+
.set_text(&format!(
194+
"Error while creating JSON for file {}!\n{}",
195+
&path, e
196+
))
197+
.show_alert()
198+
.unwrap();
199+
}
200+
},
201+
};
202+
}
136203
};
137204
}
138205

@@ -262,17 +329,34 @@ impl Sandbox for ApplicationContext {
262329
if self.differences.is_empty() {
263330
diff_text = Text::new("No differences detected!")
264331
}
265-
332+
266333
let diff_column = self.differences.iter().fold(
267-
Column::new()
268-
.spacing(10)
269-
.push(diff_text.size(30)),
270-
|column, theme| column.push(Text::new(format!("{}", theme))),
334+
Column::new().spacing(10).push(diff_text.size(30)),
335+
|column, theme| column.push(Text::new(format!("- {}", theme))),
271336
);
272337

273338
content = content
274339
.push(Rule::horizontal(20).style(self.theme))
275340
.push(diff_column);
341+
342+
if !self.differences.is_empty() {
343+
let btn_export = Button::new(
344+
&mut self.btn_export,
345+
Text::new("Export").horizontal_alignment(alignment::Horizontal::Center),
346+
)
347+
.padding(10)
348+
.min_width(100)
349+
.on_press(Message::ExportPressed)
350+
.style(self.theme);
351+
352+
content = content.push(
353+
Column::new()
354+
.width(Length::Fill)
355+
.align_items(Alignment::End)
356+
.spacing(20)
357+
.push(btn_export),
358+
);
359+
}
276360
}
277361

278362
content = content

0 commit comments

Comments
 (0)