use std::cell::{Cell, RefCell};
use std::rc::Rc;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ask {
pub text: String,
pub confirm: String,
pub cancel: String,
}
impl Ask {
pub fn new(
text: impl Into<String>,
confirm: impl Into<String>,
cancel: impl Into<String>,
) -> Self {
Self {
text: text.into(),
confirm: confirm.into(),
cancel: cancel.into(),
}
}
}
#[derive(Clone)]
pub enum Verdict {
Allow,
Block,
Ask(Ask, Decision),
}
impl Verdict {
pub fn ask(ask: Ask) -> Self {
Verdict::Ask(ask, Decision::new())
}
}
#[derive(Clone)]
pub struct Decision {
inner: Rc<Inner>,
}
type Resume = Box<dyn FnOnce(bool)>;
struct Inner {
settled: Cell<bool>,
answer: Cell<bool>,
resume: RefCell<Option<Resume>>,
}
impl Default for Decision {
fn default() -> Self {
Self::new()
}
}
impl Decision {
pub fn new() -> Self {
Self {
inner: Rc::new(Inner {
settled: Cell::new(false),
answer: Cell::new(false),
resume: RefCell::new(None),
}),
}
}
pub fn allow(&self) {
self.settle(true);
}
pub fn block(&self) {
self.settle(false);
}
pub fn is_settled(&self) -> bool {
self.inner.settled.get()
}
pub fn on_answer(&self, resume: impl FnOnce(bool) + 'static) {
if self.inner.settled.get() {
resume(self.answered_with());
return;
}
*self.inner.resume.borrow_mut() = Some(Box::new(resume));
}
fn settle(&self, allowed: bool) {
if self.inner.settled.replace(true) {
return;
}
self.inner.answer.set(allowed);
let resume = self.inner.resume.borrow_mut().take();
if let Some(resume) = resume {
resume(allowed);
}
}
fn answered_with(&self) -> bool {
self.inner.answer.get()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_answer_reaches_whoever_was_waiting() {
let seen = Rc::new(Cell::new(None));
let decision = Decision::new();
let recorder = seen.clone();
decision.on_answer(move |allowed| recorder.set(Some(allowed)));
decision.allow();
assert_eq!(seen.get(), Some(true));
}
#[test]
fn an_answer_that_arrived_first_is_not_lost() {
let decision = Decision::new();
decision.block();
let seen = Rc::new(Cell::new(None));
let recorder = seen.clone();
decision.on_answer(move |allowed| recorder.set(Some(allowed)));
assert_eq!(
seen.get(),
Some(false),
"a guard may settle its own token before it returns"
);
}
#[test]
fn a_second_answer_changes_nothing() {
let count = Rc::new(Cell::new(0));
let decision = Decision::new();
let counter = count.clone();
decision.on_answer(move |_| counter.set(counter.get() + 1));
decision.allow();
decision.block();
assert_eq!(count.get(), 1, "a dialog closed twice must not answer twice");
}
}