diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-01-15 12:31:49 +0200 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-01-20 15:58:59 +0200 |
commit | 7f8c6383610c5c8595b99a35b359fe366592e195 (patch) | |
tree | 58bdf7eadef9d5e94de0133e58efc6a4e5dc9f5a /ui | |
parent | 853fe14128751a20ace66db999d6b9b12ddca646 (diff) |
melib/imap: add mailbox creation ability
Diffstat (limited to 'ui')
-rw-r--r-- | ui/src/conf/accounts.rs | 199 | ||||
-rw-r--r-- | ui/src/execute.rs | 44 | ||||
-rw-r--r-- | ui/src/lib.rs | 1 | ||||
-rw-r--r-- | ui/src/plugins/backend.rs | 4 | ||||
-rw-r--r-- | ui/src/state.rs | 8 | ||||
-rw-r--r-- | ui/src/types.rs | 2 |
6 files changed, 178 insertions, 80 deletions
diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs index 116d1200..67df5e2a 100644 --- a/ui/src/conf/accounts.rs +++ b/ui/src/conf/accounts.rs @@ -384,7 +384,6 @@ impl Account { folder_names.insert(f.hash(), f.path().to_string()); } - let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new(); let mut tree: Vec<FolderNode> = Vec::new(); let mut collection: Collection = Collection::new(Default::default()); for (h, f) in ref_folders.iter() { @@ -392,28 +391,6 @@ impl Account { /* Skip unsubscribed folder */ continue; } - if f.parent().is_none() { - fn rec(h: FolderHash, ref_folders: &FnvHashMap<FolderHash, Folder>) -> FolderNode { - let mut node = FolderNode { - hash: h, - kids: Vec::new(), - }; - for &c in ref_folders[&h].children() { - node.kids.push(rec(c, ref_folders)); - } - node - }; - - tree.push(rec(*h, &ref_folders)); - for &c in f.children() { - stack.push(c); - } - while let Some(next) = stack.pop() { - for c in ref_folders[&next].children() { - stack.push(*c); - } - } - } folders.insert( *h, MailboxEntry::Parsing(Mailbox::new(f.clone(), &FnvHashMap::default()), 0, 0), @@ -430,40 +407,7 @@ impl Account { collection.threads.insert(*h, Threads::default()); } - tree.sort_unstable_by(|a, b| { - if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") { - std::cmp::Ordering::Greater - } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") { - std::cmp::Ordering::Less - } else { - ref_folders[&a.hash] - .path() - .cmp(&ref_folders[&b.hash].path()) - } - }); - - let mut stack: SmallVec<[Option<&FolderNode>; 8]> = SmallVec::new(); - for n in tree.iter_mut() { - folders_order.push(n.hash); - n.kids.sort_unstable_by(|a, b| { - if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") { - std::cmp::Ordering::Greater - } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") { - std::cmp::Ordering::Less - } else { - ref_folders[&a.hash] - .path() - .cmp(&ref_folders[&b.hash].path()) - } - }); - stack.extend(n.kids.iter().rev().map(Some)); - while let Some(Some(next)) = stack.pop() { - folders_order.push(next.hash); - stack.extend(next.kids.iter().rev().map(Some)); - } - } - drop(stack); - + build_folders_order(&folder_confs, &mut tree, &ref_folders, &mut folders_order); self.folders = folders; self.ref_folders = ref_folders; self.folder_confs = folder_confs; @@ -784,11 +728,7 @@ impl Account { self.folders.is_empty() } pub fn list_folders(&self) -> Vec<Folder> { - let mut folders = if let Ok(folders) = self.backend.read().unwrap().folders() { - folders - } else { - return Vec::new(); - }; + let mut folders = self.ref_folders.clone(); let folder_confs = &self.folder_confs; //debug!("folder renames: {:?}", folder_renames); for f in folders.values_mut() { @@ -1015,7 +955,68 @@ impl Account { } pub fn folder_operation(&mut self, path: &str, op: FolderOperation) -> Result<()> { - Err(MeliError::new("Not implemented.")) + match op { + FolderOperation::Create => { + if self.settings.account.read_only() { + Err(MeliError::new("Account is read-only.")) + } else { + let mut folder = self + .backend + .write() + .unwrap() + .create_folder(path.to_string())?; + let mut new = FileFolderConf::default(); + new.folder_conf.subscribe = super::ToggleFlag::InternalVal(true); + new.folder_conf.usage = if folder.special_usage() != SpecialUsageMailbox::Normal + { + Some(folder.special_usage()) + } else { + let tmp = SpecialUsageMailbox::detect_usage(folder.name()); + if tmp != Some(SpecialUsageMailbox::Normal) && tmp != None { + let _ = folder.set_special_usage(tmp.unwrap()); + } + tmp + }; + + self.folder_confs.insert(folder.hash(), new); + self.folder_names + .insert(folder.hash(), folder.path().to_string()); + self.folders.insert( + folder.hash(), + MailboxEntry::Parsing( + Mailbox::new(folder.clone(), &FnvHashMap::default()), + 0, + 0, + ), + ); + self.workers.insert( + folder.hash(), + Account::new_worker( + folder.clone(), + &mut self.backend, + &self.work_context, + self.notify_fn.clone(), + ), + ); + self.collection + .threads + .insert(folder.hash(), Threads::default()); + self.ref_folders.insert(folder.hash(), folder); + build_folders_order( + &self.folder_confs, + &mut self.tree, + &self.ref_folders, + &mut self.folders_order, + ); + Ok(()) + } + } + FolderOperation::Delete => Err(MeliError::new("Not implemented.")), + FolderOperation::Subscribe => Err(MeliError::new("Not implemented.")), + FolderOperation::Unsubscribe => Err(MeliError::new("Not implemented.")), + FolderOperation::Rename(_) => Err(MeliError::new("Not implemented.")), + FolderOperation::SetPermissions(_) => Err(MeliError::new("Not implemented.")), + } } pub fn folder_confs(&self, folder_hash: FolderHash) -> &FileFolderConf { @@ -1147,3 +1148,75 @@ impl IndexMut<usize> for Account { self.folders.get_mut(&self.folders_order[index]).unwrap() } } + +fn build_folders_order( + folder_confs: &FnvHashMap<FolderHash, FileFolderConf>, + tree: &mut Vec<FolderNode>, + ref_folders: &FnvHashMap<FolderHash, Folder>, + folders_order: &mut Vec<FolderHash>, +) { + let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new(); + tree.clear(); + folders_order.clear(); + for (h, f) in ref_folders.iter() { + if !folder_confs.contains_key(&h) { + continue; + } + + if f.parent().is_none() { + fn rec(h: FolderHash, ref_folders: &FnvHashMap<FolderHash, Folder>) -> FolderNode { + let mut node = FolderNode { + hash: h, + kids: Vec::new(), + }; + for &c in ref_folders[&h].children() { + node.kids.push(rec(c, ref_folders)); + } + node + }; + + tree.push(rec(*h, &ref_folders)); + for &c in f.children() { + stack.push(c); + } + while let Some(next) = stack.pop() { + for c in ref_folders[&next].children() { + stack.push(*c); + } + } + } + } + + tree.sort_unstable_by(|a, b| { + if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") { + std::cmp::Ordering::Greater + } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") { + std::cmp::Ordering::Less + } else { + ref_folders[&a.hash] + .path() + .cmp(&ref_folders[&b.hash].path()) + } + }); + + let mut stack: SmallVec<[Option<&FolderNode>; 8]> = SmallVec::new(); + for n in tree.iter_mut() { + folders_order.push(n.hash); + n.kids.sort_unstable_by(|a, b| { + if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") { + std::cmp::Ordering::Greater + } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") { + std::cmp::Ordering::Less + } else { + ref_folders[&a.hash] + .path() + .cmp(&ref_folders[&b.hash].path()) + } + }); + stack.extend(n.kids.iter().rev().map(Some)); + while let Some(Some(next)) = stack.pop() { + folders_order.push(next.hash); + stack.extend(next.kids.iter().rev().map(Some)); + } + } +} diff --git a/ui/src/execute.rs b/ui/src/execute.rs index 2d50686a..e6932e45 100644 --- a/ui/src/execute.rs +++ b/ui/src/execute.rs @@ -23,7 +23,7 @@ */ use melib::backends::FolderOperation; pub use melib::thread::{SortField, SortOrder}; -use nom::{digit, not_line_ending}; +use nom::{digit, not_line_ending, IResult}; use std; pub mod actions; pub mod history; @@ -45,6 +45,26 @@ macro_rules! define_commands { }; } +pub fn quoted_argument(input: &[u8]) -> IResult<&[u8], &str> { + if input.is_empty() { + return IResult::Error(nom::ErrorKind::Custom(0)); + } + + if input[0] == b'"' { + let mut i = 1; + while i < input.len() { + if input[i] == b'\"' && input[i - 1] != b'\\' { + return IResult::Done(&input[i + 1..], unsafe { + std::str::from_utf8_unchecked(&input[1..i]) + }); + } + i += 1; + } + return IResult::Error(nom::ErrorKind::Custom(0)); + } else { + return map_res!(input, is_not!(" "), std::str::from_utf8); + } +} define_commands!([ { tags: ["set"], desc: "set [seen/unseen], toggles message's Seen flag.", @@ -179,9 +199,9 @@ define_commands!([ ws!(tag!("pipe")) >> bin: map_res!(is_not!(" "), std::str::from_utf8) >> is_a!(" ") - >> args: separated_list!(is_a!(" "), is_not!(" ")) + >> args: separated_list!(is_a!(" "), quoted_argument) >> ({ - View(Pipe(bin.to_string(), args.into_iter().map(|v| String::from_utf8(v.to_vec()).unwrap()).collect::<Vec<String>>())) + View(Pipe(bin.to_string(), args.into_iter().map(String::from).collect::<Vec<String>>())) })) | do_parse!( ws!(tag!("pipe")) >> bin: ws!(map_res!(is_not!(" "), std::str::from_utf8)) @@ -233,7 +253,7 @@ define_commands!([ named!( create_folder<Action>, do_parse!( ws!(tag!("create-folder")) - >> account: map_res!(is_not!(" "), std::str::from_utf8) + >> account: quoted_argument >> is_a!(" ") >> path: map_res!(call!(not_line_ending), std::str::from_utf8) >> (Folder(account.to_string(), path.to_string(), FolderOperation::Create)) @@ -247,7 +267,7 @@ define_commands!([ named!( sub_folder<Action>, do_parse!( ws!(tag!("subscribe-folder")) - >> account: map_res!(is_not!(" "), std::str::from_utf8) + >> account: quoted_argument >> is_a!(" ") >> path: map_res!(call!(not_line_ending), std::str::from_utf8) >> (Folder(account.to_string(), path.to_string(), FolderOperation::Subscribe)) @@ -261,7 +281,7 @@ define_commands!([ named!( unsub_folder<Action>, do_parse!( ws!(tag!("unsubscribe-folder")) - >> account: map_res!(is_not!(" "), std::str::from_utf8) + >> account: quoted_argument >> is_a!(" ") >> path: map_res!(call!(not_line_ending), std::str::from_utf8) >> (Folder(account.to_string(), path.to_string(), FolderOperation::Unsubscribe)) @@ -275,9 +295,9 @@ define_commands!([ named!( rename_folder<Action>, do_parse!( ws!(tag!("rename-folder")) - >> account: map_res!(is_not!(" "), std::str::from_utf8) + >> account: quoted_argument >> is_a!(" ") - >> src: map_res!(is_not!(" "), std::str::from_utf8) + >> src: quoted_argument >> is_a!(" ") >> dest: map_res!(call!(not_line_ending), std::str::from_utf8) >> (Folder(account.to_string(), src.to_string(), FolderOperation::Rename(dest.to_string()))) @@ -291,9 +311,9 @@ define_commands!([ named!( delete_folder<Action>, do_parse!( ws!(tag!("delete-folder")) - >> account: map_res!(is_not!(" "), std::str::from_utf8) + >> account: quoted_argument >> is_a!(" ") - >> path: map_res!(call!(not_line_ending), std::str::from_utf8) + >> path: quoted_argument >> (Folder(account.to_string(), path.to_string(), FolderOperation::Delete)) ) ); @@ -305,7 +325,7 @@ define_commands!([ named!( reindex<Action>, do_parse!( ws!(tag!("reindex")) - >> account: map_res!(is_not!(" "), std::str::from_utf8) + >> account: quoted_argument >> (AccountAction(account.to_string(), ReIndex)) ) ); @@ -329,7 +349,7 @@ define_commands!([ do_parse!( ws!(tag!("save-attachment")) >> idx: map_res!(map_res!(is_not!(" "), std::str::from_utf8), usize::from_str) - >> path: ws!(map_res!(call!(not_line_ending), std::str::from_utf8)) + >> path: ws!(quoted_argument) >> (View(SaveAttachment(idx, path.to_string()))) ) ); diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 98d23ed6..7ec3ad48 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -231,7 +231,6 @@ pub mod timer { impl PosixTimer { pub fn rearm(&mut self) { - debug!("posixtimer rearm"); let spec = itimerspec { it_interval: timespec { tv_sec: self.interval.as_secs().try_into().unwrap_or(0), diff --git a/ui/src/plugins/backend.rs b/ui/src/plugins/backend.rs index e1062ca0..4731a1ee 100644 --- a/ui/src/plugins/backend.rs +++ b/ui/src/plugins/backend.rs @@ -102,7 +102,7 @@ impl MailBackend for PluginBackend { channel.expect_ack().unwrap(); loop { let read_val: Result<PluginResult<Option<Vec<SimpleEnvelope>>>> = - debug!(channel.from_read()); + channel.from_read(); match read_val.map(Into::into).and_then(std::convert::identity) { Ok(Some(a)) => { tx.send(AsyncStatus::Payload(Ok(a @@ -300,7 +300,7 @@ impl BackendOp for PluginOp { debug!(channel.expect_ack())?; channel.write_ref(&rmpv::ValueRef::Integer(self.hash.into()))?; debug!(channel.expect_ack())?; - let bytes: Result<PluginResult<String>> = debug!(channel.from_read()); + let bytes: Result<PluginResult<String>> = channel.from_read(); self.bytes = Some(bytes.map(Into::into).and_then(std::convert::identity)?); if let Some(ref bytes) = self.bytes { debug!(Envelope::from_bytes(bytes.as_bytes(), None)); diff --git a/ui/src/state.rs b/ui/src/state.rs index 1067f2e0..518c242f 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -254,7 +254,6 @@ impl State { let sender = sender; loop { thread::park(); - debug!("unparked"); sender.send(ThreadEvent::Pulse).unwrap(); thread::sleep(std::time::Duration::from_millis(100)); @@ -586,6 +585,13 @@ impl State { self.context.replies.push_back(UIEvent::StatusEvent( StatusEvent::DisplayMessage(e.to_string()), )); + } else { + self.context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(format!( + "{} succesfully created in `{}`", + path, account_name + )), + )); } } else { self.context.replies.push_back(UIEvent::StatusEvent( diff --git a/ui/src/types.rs b/ui/src/types.rs index e8c76ec6..c2e2a985 100644 --- a/ui/src/types.rs +++ b/ui/src/types.rs @@ -181,7 +181,7 @@ pub mod segment_tree { let height = (f64::from(u32::try_from(val.len()).unwrap_or(0))) .log2() .ceil() as u32; - let max_size = 2 * (2_usize.pow(height)) - 1; + let max_size = 2 * (2_usize.pow(height)); let mut segment_tree: SmallVec<[u8; 1024]> = SmallVec::from_iter(core::iter::repeat(0).take(max_size)); |