diff --git a/melib/Cargo.toml b/melib/Cargo.toml index b33039ff..edb71bcd 100644 --- a/melib/Cargo.toml +++ b/melib/Cargo.toml @@ -20,3 +20,4 @@ data-encoding = "2.1.1" encoding = "0.2.33" bitflags = "1.0" termion = "1.5.1" +chan = "0.1.21" diff --git a/melib/src/async.rs b/melib/src/async.rs new file mode 100644 index 00000000..fd5eb046 --- /dev/null +++ b/melib/src/async.rs @@ -0,0 +1,86 @@ +use chan; +use std::thread; + +#[derive(Debug)] +pub enum AsyncStatus { + NoUpdate, + Finished, + ProgressReport(usize), +} + +#[derive(Debug)] +pub struct AsyncBuilder { + tx: chan::Sender, + rx: chan::Receiver, +} + +#[derive(Debug)] +pub struct Async { + value: Option, + worker: Option>, + tx: chan::Sender, + rx: chan::Receiver, +} + + + +impl AsyncBuilder { + pub fn new() -> Self { + let (sender, receiver) = chan::sync(::std::mem::size_of::()); + AsyncBuilder { + tx: sender, + rx: receiver, + } + } + pub fn tx(&mut self) -> chan::Sender { + self.tx.clone() + } + pub fn rx(&mut self) -> chan::Receiver { + self.rx.clone() + } + pub fn build(self, worker: thread::JoinHandle) -> Async { + Async { + worker: Some(worker), + value: None, + tx: self.tx, + rx: self.rx, + } + } +} + +impl Async { + pub fn extract(self) -> T { + self.value.unwrap() + } + pub fn poll(&mut self) -> Result { + if self.value.is_some() { + return Ok(AsyncStatus::Finished); + } + //self.tx.send(true); + let rx = &self.rx; + chan_select! { + default => { + return Ok(AsyncStatus::NoUpdate); + }, + rx.recv() -> r => { + match r { + Some(AsyncStatus::Finished) => { + }, + Some(a) => { + eprintln!("async got {:?}", a); + return Ok(a); + } + _ => { + return Err(()); + }, + } + + }, + } + let v = self.worker.take().unwrap().join().unwrap(); + self.value = Some(v); + eprintln!("worker joined"); + return Ok(AsyncStatus::Finished); + } +} + diff --git a/melib/src/conf/mod.rs b/melib/src/conf/mod.rs index 9ee370e4..1e718b8c 100644 --- a/melib/src/conf/mod.rs +++ b/melib/src/conf/mod.rs @@ -111,7 +111,8 @@ impl FileSettings { let mut s = Config::new(); let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml)); - // TODO: Return result + // No point in returning without a config file. + // TODO: Error and exit instead of panic. s.unwrap().deserialize().unwrap() } } diff --git a/melib/src/lib.rs b/melib/src/lib.rs index 41954a68..d953ff45 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -21,6 +21,8 @@ pub mod conf; pub mod error; pub mod mailbox; +pub mod utilities; +pub mod async; #[macro_use] extern crate serde_derive; @@ -31,6 +33,8 @@ extern crate chrono; extern crate data_encoding; extern crate encoding; extern crate memmap; +#[macro_use] +extern crate chan; #[macro_use] extern crate bitflags; @@ -41,3 +45,4 @@ pub use mailbox::*; pub use error::{MeliError, Result}; pub use mailbox::backends::{Backends, RefreshEvent, RefreshEventConsumer}; pub use mailbox::email::{Envelope, Flag}; +pub use utilities::*; diff --git a/melib/src/mailbox/accounts.rs b/melib/src/mailbox/accounts.rs index 9dc5812f..000acfe6 100644 --- a/melib/src/mailbox/accounts.rs +++ b/melib/src/mailbox/accounts.rs @@ -22,13 +22,19 @@ use conf::{AccountSettings, Folder}; use mailbox::backends::{Backends, RefreshEventConsumer}; use mailbox::*; +use async::*; use std::ops::{Index, IndexMut}; +use std::result; + +pub type Worker = Option>>>; #[derive(Debug)] pub struct Account { name: String, folders: Vec>>, + workers: Vec, + sent_folder: Option, pub settings: AccountSettings, @@ -43,13 +49,17 @@ impl Account { .iter() .position(|x| *x.path() == settings.sent_folder); let mut folders = Vec::with_capacity(settings.folders.len()); - for _ in 0..settings.folders.len() { - folders.push(None); - } + let mut workers = Vec::new(); let backend = backends.get(settings.format()); + for f in &settings.folders { + folders.push(None); + let mut handle = backend.get(&f); + workers.push(Some(handle)); + } Account { name: name, folders: folders, + workers: workers, sent_folder: sent_folder, @@ -71,24 +81,16 @@ impl Account { pub fn name(&self) -> &str { &self.name } -} - -impl Index for Account { - type Output = Option>; - fn index(&self, index: usize) -> &Option> { - &self.folders[index] + pub fn workers(&mut self) -> &mut Vec { + &mut self.workers } -} - -impl IndexMut for Account { - fn index_mut(&mut self, index: usize) -> &mut Option> { - if self.folders[index].is_none() { + fn load_mailbox(&mut self, index: usize, envelopes: Result>) -> () { let folder = &self.settings.folders[index]; if self.sent_folder.is_some() { let id = self.sent_folder.unwrap(); if id == index { self.folders[index] = - Some(Mailbox::new(folder, &None, self.backend.get(&folder))); + Some(Mailbox::new(folder, &None, envelopes)); } else { let (sent, cur) = { let ptr = self.folders.as_mut_ptr(); @@ -102,14 +104,54 @@ impl IndexMut for Account { }; let sent_path = &self.settings.folders[id]; if sent[0].is_none() { - sent[0] = Some(Mailbox::new(sent_path, &None, self.backend.get(&folder))); + sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone())); } - cur[0] = Some(Mailbox::new(folder, &sent[0], self.backend.get(folder))); + cur[0] = Some(Mailbox::new(folder, &sent[0], envelopes)); } } else { - self.folders[index] = Some(Mailbox::new(folder, &None, self.backend.get(&folder))); + self.folders[index] = Some(Mailbox::new(folder, &None, envelopes)); }; - } - &mut self.folders[index] + } + + pub fn status(&mut self, index: usize) -> result::Result<(), usize> { + match self.workers[index].as_mut() { + None => { return Ok(()); }, + Some(ref mut w) => { + match w.poll() { + Ok(AsyncStatus::NoUpdate) => { + return Err(0); + }, + Ok(AsyncStatus::Finished) => { + }, + Ok(AsyncStatus::ProgressReport(n)) => { + return Err(n); + }, + a => { + eprintln!("{:?}", a); + return Err(0); + } + } + }, + }; + let m = self.workers[index].take().unwrap().extract(); + self.load_mailbox(index, m); + self.workers[index] = None; + Ok(()) + } + +} + +impl Index for Account { + type Output = Result; + fn index(&self, index: usize) -> &Result { + &self.folders[index].as_ref().expect("BUG: Requested mailbox that is not yet available.") + + } +} + +/// Will panic if mailbox hasn't loaded, ask `status()` first. +impl IndexMut for Account { + fn index_mut(&mut self, index: usize) -> &mut Result { + self.folders[index].as_mut().expect("BUG: Requested mailbox that is not yet available.") } } diff --git a/melib/src/mailbox/backends/imap.rs b/melib/src/mailbox/backends/imap.rs index ac733865..dc6193c7 100644 --- a/melib/src/mailbox/backends/imap.rs +++ b/melib/src/mailbox/backends/imap.rs @@ -21,6 +21,7 @@ use conf::Folder; use error::Result; +use async::*; use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer}; use mailbox::email::{Envelope, Flag}; @@ -57,7 +58,7 @@ impl BackendOp for ImapOp { pub struct ImapType {} impl MailBackend for ImapType { - fn get(&self, _folder: &Folder) -> Result> { + fn get(&self, _folder: &Folder) -> Async>> { unimplemented!(); } fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () { diff --git a/melib/src/mailbox/backends/maildir.rs b/melib/src/mailbox/backends/maildir.rs index 46bd1ce1..2235895e 100644 --- a/melib/src/mailbox/backends/maildir.rs +++ b/melib/src/mailbox/backends/maildir.rs @@ -19,6 +19,7 @@ * along with meli. If not, see . */ +use async::*; use conf::Folder; use error::{MeliError, Result}; use mailbox::backends::{ @@ -40,6 +41,8 @@ use std::thread; extern crate crossbeam; use memmap::{Mmap, Protection}; use std::path::PathBuf; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; /// `BackendOp` implementor for Maildir #[derive(Debug, Default)] @@ -116,10 +119,11 @@ impl BackendOp for MaildirOp { #[derive(Debug)] pub struct MaildirType { path: String, + idx: (usize, usize), } impl MailBackend for MaildirType { - fn get(&self, folder: &Folder) -> Result> { + fn get(&self, folder: &Folder) -> Async>> { self.multicore(4, folder) } fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () { @@ -145,11 +149,15 @@ impl MailBackend for MaildirType { match rx.recv() { Ok(event) => match event { DebouncedEvent::Create(pathbuf) => { + let path = pathbuf.parent().unwrap().to_str().unwrap(); + + let mut hasher = DefaultHasher::new(); + hasher.write(path.as_bytes()); sender.send(RefreshEvent { folder: format!( - "{}", - pathbuf.parent().unwrap().to_str().unwrap() + "{}", path ), + hash: hasher.finish(), }); } _ => {} @@ -163,9 +171,10 @@ impl MailBackend for MaildirType { } impl MaildirType { - pub fn new(path: &str) -> Self { + pub fn new(path: &str, idx: (usize, usize)) -> Self { MaildirType { path: path.to_string(), + idx: idx, } } fn is_valid(f: &Folder) -> Result<()> { @@ -183,8 +192,19 @@ impl MaildirType { } Ok(()) } - pub fn multicore(&self, cores: usize, folder: &Folder) -> Result> { - MaildirType::is_valid(folder)?; + pub fn multicore(&self, cores: usize, folder: &Folder) -> Async>> { + + let mut w = AsyncBuilder::new(); + let handle = { + let tx = w.tx(); + // TODO: Avoid clone + let folder = folder.clone(); + + + thread::Builder::new() + .name(format!("parsing {:?}", folder)) + .spawn(move || { + MaildirType::is_valid(&folder)?; let path = folder.path(); let mut path = PathBuf::from(path); path.push("cur"); @@ -208,19 +228,26 @@ impl MaildirType { count }; for chunk in files.chunks(chunk_size) { + let mut tx = tx.clone(); let s = scope.spawn(move || { + let len = chunk.len(); + let size = if len <= 100 { 100 } else { (len / 100) * 100}; let mut local_r: Vec = Vec::with_capacity(chunk.len()); - for e in chunk { - let e_copy = e.to_string(); - if let Some(mut e) = - Envelope::from_token(Box::new(BackendOpGenerator::new(Box::new( - move || Box::new(MaildirOp::new(e_copy.clone())), - )))) { - if e.populate_headers().is_err() { - continue; - } - local_r.push(e); + for c in chunk.chunks(size) { + let len = c.len(); + for e in c { + let e_copy = e.to_string(); + if let Some(mut e) = + Envelope::from_token(Box::new(BackendOpGenerator::new(Box::new( + move || Box::new(MaildirOp::new(e_copy.clone())), + )))) { + if e.populate_headers().is_err() { + continue; + } + local_r.push(e); + } } + tx.send(AsyncStatus::ProgressReport(len)); } local_r }); @@ -232,6 +259,10 @@ impl MaildirType { let mut result = t.join(); r.append(&mut result); } + tx.send(AsyncStatus::Finished); Ok(r) + }).unwrap() + }; + w.build(handle) } } diff --git a/melib/src/mailbox/backends/mbox.rs b/melib/src/mailbox/backends/mbox.rs index d140a0c7..cb28dba7 100644 --- a/melib/src/mailbox/backends/mbox.rs +++ b/melib/src/mailbox/backends/mbox.rs @@ -21,6 +21,7 @@ use conf::Folder; use error::Result; +use async::*; use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer}; use mailbox::email::{Envelope, Flag}; @@ -57,7 +58,7 @@ impl BackendOp for MboxOp { pub struct MboxType {} impl MailBackend for MboxType { - fn get(&self, _folder: &Folder) -> Result> { + fn get(&self, _folder: &Folder) -> Async>> { unimplemented!(); } fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () { diff --git a/melib/src/mailbox/backends/mod.rs b/melib/src/mailbox/backends/mod.rs index 744f2312..cbaee595 100644 --- a/melib/src/mailbox/backends/mod.rs +++ b/melib/src/mailbox/backends/mod.rs @@ -24,6 +24,7 @@ pub mod mbox; use conf::Folder; use error::Result; +use async::*; use mailbox::backends::imap::ImapType; use mailbox::backends::maildir::MaildirType; use mailbox::backends::mbox::MboxType; @@ -47,7 +48,7 @@ impl Backends { }; b.register( "maildir".to_string(), - Box::new(|| Box::new(MaildirType::new(""))), + Box::new(|| Box::new(MaildirType::new("", (0, 0)))), ); b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new("")))); b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new("")))); @@ -69,6 +70,7 @@ impl Backends { } pub struct RefreshEvent { + pub hash: u64, pub folder: String, } @@ -87,7 +89,7 @@ impl RefreshEventConsumer { } } pub trait MailBackend: ::std::fmt::Debug { - fn get(&self, folder: &Folder) -> Result>; + fn get(&self, folder: &Folder) -> Async>>; fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> (); //fn new(folders: &Vec) -> Box; //login function diff --git a/melib/src/utilities.rs b/melib/src/utilities.rs new file mode 100644 index 00000000..438014c9 --- /dev/null +++ b/melib/src/utilities.rs @@ -0,0 +1,11 @@ + +pub trait ProgressTracker { + fn new(s: String, total_work: usize) -> Self; + + fn add_work(&mut self, n: usize) -> (); + fn set_work(&mut self, n: usize) -> (); + fn work(&mut self, n: usize) -> (); + + fn percentage(&self) -> usize; + fn description(&self) -> &str; +} diff --git a/src/bin.rs b/src/bin.rs index f28b6904..53a411cf 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -68,6 +68,7 @@ fn make_input_thread( }) .unwrap() } + fn main() { /* Lock all stdio outs */ //let _stdout = stdout(); @@ -182,8 +183,9 @@ fn main() { }, } }, - ThreadEvent::RefreshMailbox { name : n } => { - state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(n.clone())}); + ThreadEvent::RefreshMailbox { hash : h } => { + eprintln!("got refresh mailbox hash {:x}", h); + //state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(n.clone())}); state.redraw(); /* Don't handle this yet. */ }, diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index f9b81c2d..dca64a7a 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -1,9 +1,10 @@ use super::*; + const MAX_COLS: usize = 500; /// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a -/// `Pager`. +/// `MailView`. pub struct MailListing { /// (x, y, z): x is accounts, y is folders, z is index inside a folder. cursor_pos: (usize, usize, usize), @@ -24,7 +25,7 @@ impl MailListing { /* TODO: Make this configurable */ fn make_entry_string(e: &Envelope, idx: usize) -> String { format!( - "{} {} {:.85}", + "{} {} {}", idx, &e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(), e.subject() @@ -55,17 +56,25 @@ impl MailListing { let threaded = context.accounts[self.cursor_pos.0] .runtime_settings .threaded; - // Get mailbox as a reference. - let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] - .as_ref() - .unwrap() - .as_ref() - .unwrap(); // Inform State that we changed the current folder view. context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)), }); + // Get mailbox as a reference. + // + loop { + eprintln!("loop round"); + match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { + Ok(()) => { break; }, + Err(a) => { + eprintln!("status returned {:?}", a); + } + } + } + let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] + .as_ref() + .unwrap(); self.length = if threaded { mailbox.threaded_collection.len() @@ -81,7 +90,7 @@ impl MailListing { Color::Default, ((0, 0), (MAX_COLS - 1, 0)), true, - ); + ); self.content = content; return; } @@ -90,7 +99,7 @@ impl MailListing { if threaded { let mut indentations: Vec = Vec::with_capacity(6); let mut thread_idx = 0; // needed for alternate thread colors - /* Draw threaded view. */ + /* Draw threaded view. */ let mut iter = mailbox.threaded_collection.iter().enumerate().peekable(); let len = mailbox .threaded_collection @@ -143,13 +152,13 @@ impl MailListing { container, &indentations, len, - ), - &mut content, - fg_color, - bg_color, - ((0, idx), (MAX_COLS - 1, idx)), - false, - ); + ), + &mut content, + fg_color, + bg_color, + ((0, idx), (MAX_COLS - 1, idx)), + false, + ); for x in x..MAX_COLS { content[(x, idx)].set_ch(' '); content[(x, idx)].set_bg(bg_color); @@ -199,7 +208,7 @@ impl MailListing { bg_color, ((0, y), (MAX_COLS - 1, y)), false, - ); + ); for x in x..MAX_COLS { content[(x, y)].set_ch(' '); @@ -217,8 +226,6 @@ impl MailListing { .runtime_settings .threaded; let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] - .as_ref() - .unwrap() .as_ref() .unwrap(); let envelope: &Envelope = if threaded { @@ -563,6 +570,12 @@ impl Component for MailListing { self.dirty = true; self.view = None; } + UIEventType::MailboxUpdate((ref idxa, ref idxf)) => { + if *idxa == self.new_cursor_pos.1 && *idxf == self.new_cursor_pos.0 { + self.refresh_mailbox(context); + self.dirty = true; + } + } UIEventType::ChangeMode(UIMode::Normal) => { self.dirty = true; } diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 74546100..8c1a47cf 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -61,8 +61,6 @@ impl Component for MailView { .runtime_settings .threaded; let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1] - .as_ref() - .unwrap() .as_ref() .unwrap(); let envelope_idx: usize = if threaded { @@ -157,8 +155,6 @@ impl Component for MailView { let buf = { let mailbox_idx = self.coordinates; // coordinates are mailbox idxs let mailbox = &mut context.accounts[mailbox_idx.0][mailbox_idx.1] - .as_ref() - .unwrap() .as_ref() .unwrap(); let envelope: &Envelope = &mailbox.collection[envelope_idx]; @@ -268,8 +264,6 @@ impl Component for MailView { .runtime_settings .threaded; let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1] - .as_ref() - .unwrap() .as_ref() .unwrap(); let envelope_idx: usize = if threaded { @@ -339,8 +333,6 @@ impl Component for MailView { .runtime_settings .threaded; let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1] - .as_ref() - .unwrap() .as_ref() .unwrap(); let envelope_idx: usize = if threaded { diff --git a/ui/src/components/mod.rs b/ui/src/components/mod.rs index 770f04e7..dc56caaf 100644 --- a/ui/src/components/mod.rs +++ b/ui/src/components/mod.rs @@ -31,7 +31,7 @@ pub mod notifications; pub mod utilities; pub use mail::*; -pub use utilities::*; +pub use self::utilities::*; use super::cells::{CellBuffer, Color}; use super::position::Area; @@ -138,6 +138,14 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, } let mut src_x = get_x(upper_left!(src)); let mut src_y = get_y(upper_left!(src)); + let (cols, rows) = grid_src.size(); + if src_x >= cols || src_y >= rows { + eprintln!( + "DEBUG: src area outside of grid_src in copy_area", + ); + return; + + } for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) { 'for_x: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) { diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index f605ea77..f28ddf05 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -413,9 +413,13 @@ impl Component for StatusBar { self.container.rcv_event(event, context); match &event.event_type { UIEventType::RefreshMailbox((ref idx_a, ref idx_f)) => { + match context.accounts[*idx_a].status(*idx_f) { + Ok(()) => {}, + Err(_) => { + return; + } + } let m = &context.accounts[*idx_a][*idx_f] - .as_ref() - .unwrap() .as_ref() .unwrap(); self.status = format!( @@ -483,3 +487,47 @@ impl Component for TextBox { return; } } + +pub struct Progress { + description: String, + total_work: usize, + finished: usize, +} + +impl Progress { + pub fn new(s: String, total_work: usize) -> Self { + Progress { + description: s, + total_work: total_work, + finished: 0, + } + } + + pub fn add_work(&mut self, n: usize) -> () { + if self.finished >= self.total_work { + return; + } + self.finished += n; + } + + pub fn percentage(&self) -> usize { + if self.total_work > 0 { + 100 * self.finished / self.total_work + } else { + 0 + } + } + + pub fn description(&self) -> &str { + &self.description + } +} + +impl Component for Progress { + fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) { + unimplemented!() + } + fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) { + return; + } +} diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 88016b4d..17f66724 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -75,7 +75,7 @@ pub enum ThreadEvent { Input(Key), /// A watched folder has been refreshed. RefreshMailbox { - name: String, + hash: u64, }, UIEventType(UIEventType), //Decode { _ }, // For gpg2 signature check @@ -83,7 +83,7 @@ pub enum ThreadEvent { impl From for ThreadEvent { fn from(event: RefreshEvent) -> Self { - ThreadEvent::RefreshMailbox { name: event.folder } + ThreadEvent::RefreshMailbox { hash: event.hash } } } @@ -109,6 +109,7 @@ pub enum UIEventType { EditDraft(File), Action(Action), StatusNotification(String), + MailboxUpdate((usize, usize)), } /// An event passed from `State` to its Entities. @@ -248,7 +249,7 @@ impl State { cursor::Hide, clear::All, cursor::Goto(1, 1) - ).unwrap(); + ).unwrap(); s.flush(); for account in &mut s.context.accounts { let sender = s.sender.clone(); @@ -455,15 +456,11 @@ impl State { /// Tries to load a mailbox's content pub fn refresh_mailbox(&mut self, account_idx: usize, folder_idx: usize) { let flag = match &mut self.context.accounts[account_idx][folder_idx] { - Some(Ok(_)) => true, - Some(Err(e)) => { + Ok(_) => true, + Err(e) => { eprintln!("error {:?}", e); false } - None => { - eprintln!("None"); - false - } }; if flag { self.rcv_event(UIEvent {