draft certificate export feature

This commit is contained in:
yggverse 2024-11-28 18:16:52 +02:00
parent 7420ce8606
commit 8a62ef46df
3 changed files with 148 additions and 1 deletions

View File

@ -1,10 +1,12 @@
mod file; mod file;
pub mod list; pub mod list;
mod name; mod name;
mod save;
use file::File; use file::File;
use list::{item::value::Value, List}; use list::{item::value::Value, List};
use name::Name; use name::Name;
use save::Save;
use super::Action; use super::Action;
use gtk::{prelude::BoxExt, Box, Orientation}; use gtk::{prelude::BoxExt, Box, Orientation};
@ -15,6 +17,7 @@ pub struct Form {
pub file: Rc<File>, pub file: Rc<File>,
pub list: Rc<List>, pub list: Rc<List>,
pub name: Rc<Name>, pub name: Rc<Name>,
pub save: Rc<Save>,
pub gobject: Box, pub gobject: Box,
} }
@ -24,9 +27,10 @@ impl Form {
/// Create new `Self` /// Create new `Self`
pub fn new(action: Rc<Action>) -> Self { pub fn new(action: Rc<Action>) -> Self {
// Init components // Init components
let file = Rc::new(File::new(action.clone()));
let list = Rc::new(List::new()); let list = Rc::new(List::new());
let name = Rc::new(Name::new(action.clone())); let name = Rc::new(Name::new(action.clone()));
let file = Rc::new(File::new(action.clone())); let save = Rc::new(Save::new(action.clone()));
// Init main container // Init main container
let gobject = Box::builder().orientation(Orientation::Vertical).build(); let gobject = Box::builder().orientation(Orientation::Vertical).build();
@ -34,11 +38,13 @@ impl Form {
gobject.append(&list.gobject); gobject.append(&list.gobject);
gobject.append(&name.gobject); gobject.append(&name.gobject);
gobject.append(&file.gobject); gobject.append(&file.gobject);
gobject.append(&save.gobject);
// Connect events // Connect events
list.on_select({ list.on_select({
let file = file.clone(); let file = file.clone();
let name = name.clone(); let name = name.clone();
let save = save.clone();
let update = action.update.clone(); let update = action.update.clone();
move |item| { move |item| {
// Change name entry visibility // Change name entry visibility
@ -47,6 +53,16 @@ impl Form {
// Change file choose button visibility // Change file choose button visibility
file.show(matches!(item, Value::ImportPem)); file.show(matches!(item, Value::ImportPem));
// Change export button visibility, update holder @TODO
match item {
Value::ProfileIdentityGeminiId(id) => {
// save.show(Some(id));
}
_ => {
// save.show(None);
}
}
// Update widget // Update widget
update.activate(); update.activate();
} }
@ -59,6 +75,7 @@ impl Form {
file, file,
list, list,
name, name,
save,
} }
} }

View File

@ -0,0 +1,126 @@
mod certificate;
use certificate::Certificate;
use super::Action;
use gtk::{
gio::{Cancellable, ListStore},
prelude::{ButtonExt, FileExt, WidgetExt},
Button, FileDialog, FileFilter, Window,
};
use std::{cell::RefCell, fs::File, io::Write, rc::Rc};
const LABEL: &str = "Export to file..";
const MARGIN: i32 = 8;
pub struct Save {
certificate: Rc<RefCell<Option<Certificate>>>,
pub gobject: Button,
}
impl Save {
// Constructors
/// Create new `Self`
pub fn new(action: Rc<Action>) -> Self {
// Init `Certificate` holder
let certificate = Rc::new(RefCell::new(None::<Certificate>));
// Init `GObject`
let gobject = Button::builder()
.label(LABEL)
.margin_top(MARGIN)
.visible(false)
.build();
// Init events
gobject.connect_clicked({
let certificate = certificate.clone();
let gobject = gobject.clone();
move |_| {
// Get certificate selected from holder
match certificate.borrow().as_ref() {
Some(certificate) => {
// Copy certificate holder values
let name = certificate.name.clone();
let data = certificate.data.clone();
// Lock open button (prevent double click)
gobject.set_sensitive(false);
// Init file filters related with PEM extension
let filters = ListStore::new::<FileFilter>();
let filter_all = FileFilter::new();
filter_all.add_pattern("*.*");
filter_all.set_name(Some("All"));
filters.append(&filter_all);
let filter_pem = FileFilter::new();
filter_pem.add_mime_type("application/x-x509-ca-cert");
filter_pem.set_name(Some("Certificate (*.pem)"));
filters.append(&filter_pem);
// Init file dialog
FileDialog::builder()
.default_filter(&filter_pem)
.filters(&filters)
.initial_name(format!("{name}.pem"))
.build()
.save(None::<&Window>, None::<&Cancellable>, {
let gobject = gobject.clone();
move |result| {
match result {
Ok(file) => match file.path() {
Some(path) => match File::create(&path) {
Ok(mut destination) => {
match destination.write_all(data.as_bytes()) {
Ok(_) => {
// @TODO
gobject.set_css_classes(&["success"]);
gobject.set_label(&format!(
"Saved to {}",
path.to_string_lossy()
))
}
Err(reason) => {
gobject.set_css_classes(&["error"]);
gobject.set_label(&reason.to_string())
}
}
}
Err(reason) => {
gobject.set_css_classes(&["error"]);
gobject.set_label(&reason.to_string())
}
},
None => todo!(),
},
Err(reason) => {
gobject.set_css_classes(&["warning"]);
gobject.set_label(reason.message())
}
}
gobject.set_sensitive(true); // unlock
}
});
}
None => todo!(), // unexpected
}
}
});
// Return activated `Self`
Self {
certificate,
gobject,
}
}
// Actions
/// Change visibility status
/// * grab focus on `is_visible`
pub fn show(&self, is_visible: bool) {
self.gobject.set_visible(is_visible)
}
}

View File

@ -0,0 +1,4 @@
pub struct Certificate {
pub name: String,
pub data: String,
}