MemoServ: add CANCEL, CHECK, and INFO
All checks were successful
CI / check (push) Successful in 3m49s
All checks were successful
CI / check (push) Successful in 3m49s
CANCEL recalls the sender's most recent unread memo to a target (one the recipient already read can't be recalled); CHECK reports whether the last memo you sent someone has been read; INFO summarises your mailbox. Adds memo_cancel/memo_check store methods and shares MAX_MEMOS from the module root instead of duplicating it in send.rs.
This commit is contained in:
parent
a26ee722d1
commit
98ecbca414
9 changed files with 118 additions and 2 deletions
|
|
@ -591,6 +591,28 @@ impl Db {
|
|||
true
|
||||
}
|
||||
|
||||
/// Recall the most recent UNREAD memo `sender` left for `account`. Returns
|
||||
/// whether one was cancelled — a memo the recipient already read can't be recalled.
|
||||
pub fn memo_cancel(&mut self, account: &str, sender: &str) -> bool {
|
||||
let sender_lc = sender.to_ascii_lowercase();
|
||||
let idx = self.accounts.get(&key(account)).and_then(|a| {
|
||||
a.memos.iter().enumerate().rev().find(|(_, m)| !m.read && m.from.to_ascii_lowercase() == sender_lc).map(|(i, _)| i)
|
||||
});
|
||||
match idx {
|
||||
Some(i) => self.memo_del(account, i),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// The read-status and timestamp of the most recent memo `sender` left for
|
||||
/// `account`, or None if they have none in that mailbox.
|
||||
pub fn memo_check(&self, account: &str, sender: &str) -> Option<(bool, u64)> {
|
||||
let sender_lc = sender.to_ascii_lowercase();
|
||||
self.accounts.get(&key(account)).and_then(|a| {
|
||||
a.memos.iter().rev().find(|m| m.from.to_ascii_lowercase() == sender_lc).map(|m| (m.read, m.ts))
|
||||
})
|
||||
}
|
||||
|
||||
/// How many unread memos an account has.
|
||||
pub fn unread_memos(&self, account: &str) -> usize {
|
||||
self.accounts.get(&key(account)).map_or(0, |a| a.memos.iter().filter(|m| !m.read).count())
|
||||
|
|
|
|||
|
|
@ -429,6 +429,12 @@ impl Store for Db {
|
|||
fn memo_del(&mut self, account: &str, index: usize) -> bool {
|
||||
Db::memo_del(self, account, index)
|
||||
}
|
||||
fn memo_cancel(&mut self, account: &str, sender: &str) -> bool {
|
||||
Db::memo_cancel(self, account, sender)
|
||||
}
|
||||
fn memo_check(&self, account: &str, sender: &str) -> Option<(bool, u64)> {
|
||||
Db::memo_check(self, account, sender)
|
||||
}
|
||||
fn unread_memos(&self, account: &str) -> usize {
|
||||
Db::unread_memos(self, account)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,27 @@
|
|||
assert_eq!(db.unread_memos("alice"), 1);
|
||||
}
|
||||
|
||||
// CANCEL recalls the sender's most recent unread memo; CHECK reports read-state.
|
||||
#[test]
|
||||
fn memo_cancel_and_check() {
|
||||
let mut db = Db::open(tmp("memo-cc"), "N1");
|
||||
db.scram_iterations = 4096;
|
||||
db.register("alice", "pw", None).unwrap();
|
||||
db.memo_send("alice", "bob", "hi").unwrap();
|
||||
db.memo_send("alice", "bob", "you there?").unwrap();
|
||||
|
||||
assert_eq!(db.memo_check("alice", "bob").map(|(read, _)| read), Some(false), "unread so far");
|
||||
assert!(db.memo_cancel("alice", "bob"), "recalls the most recent unread");
|
||||
assert_eq!(db.memo_list("alice").len(), 1, "one memo cancelled");
|
||||
|
||||
db.memo_read("alice", 0); // alice reads the remaining memo
|
||||
assert!(!db.memo_cancel("alice", "bob"), "a read memo can't be recalled");
|
||||
assert_eq!(db.memo_check("alice", "bob").map(|(read, _)| read), Some(true), "now shows read");
|
||||
|
||||
assert_eq!(db.memo_check("alice", "carol"), None, "no memo from carol");
|
||||
assert!(!db.memo_cancel("alice", "carol"));
|
||||
}
|
||||
|
||||
// A wrong code is tolerated a few times, then the code is burned so it can't
|
||||
// be ground down online even though it is short.
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue