use std::fmt;
use crate::i18n::t;
pub mod terminal;
#[cfg(target_os = "linux")]
pub mod freedesktop;
#[cfg(windows)]
pub mod toast;
#[cfg(windows)]
pub mod tray;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Listed {
pub id: String,
pub label: String,
pub payload: String,
pub live: bool,
pub needs_a_person: bool,
pub write_backs: Option<u64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Mood {
#[default]
Settled,
Working,
Look,
AtRisk,
Danger,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Trouble {
pub id: String,
pub mood: Mood,
pub summary: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Chosen {
Dismiss(String),
Quit,
}
pub trait Standing {
fn show(&self, sessions: &[Listed], troubles: &[Trouble], mood: Mood);
fn taken(&self) -> Vec<Chosen>;
fn holding(&self) -> bool {
false
}
}
pub struct Nowhere;
impl Standing for Nowhere {
fn show(&self, _sessions: &[Listed], _troubles: &[Trouble], _mood: Mood) {}
fn taken(&self) -> Vec<Chosen> {
Vec::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Weight {
Routine,
Ordinary,
Interrupt,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Report {
pub summary: String,
pub detail: Vec<String>,
pub weight: Weight,
}
impl Report {
#[must_use]
pub fn routine(summary: impl Into<String>) -> Self {
Self {
weight: Weight::Routine,
..Self::ordinary(summary)
}
}
#[must_use]
pub fn ordinary(summary: impl Into<String>) -> Self {
Self {
summary: summary.into(),
detail: Vec::new(),
weight: Weight::Ordinary,
}
}
#[must_use]
pub fn interrupt(summary: impl Into<String>) -> Self {
Self {
weight: Weight::Interrupt,
..Self::ordinary(summary)
}
}
#[must_use]
pub fn and(mut self, line: impl Into<String>) -> Self {
self.detail.push(line.into());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Choice {
WriteBack,
Discard,
Reveal,
}
impl Choice {
#[must_use]
pub fn key(self) -> &'static str {
match self {
Self::WriteBack => "write-back",
Self::Discard => "discard",
Self::Reveal => "reveal",
}
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::WriteBack => t("Write it back"),
Self::Discard => t("Discard it"),
Self::Reveal => t("Show me"),
}
}
#[must_use]
pub fn from_key(key: &str) -> Option<Self> {
[Self::WriteBack, Self::Discard, Self::Reveal]
.into_iter()
.find(|c| c.key() == key)
}
}
impl fmt::Display for Choice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.key())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Question {
pub about: String,
pub summary: String,
pub detail: Vec<String>,
pub choices: Vec<Choice>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Answer {
pub about: String,
pub choice: Choice,
}
pub trait Channel {
fn report(&self, report: &Report);
fn ask(&self, question: &Question);
fn withdraw(&self, about: &str);
fn answers(&self) -> Vec<Answer>;
fn insist(&self, report: &Report) {
self.report(report);
}
fn stay_until_seen(&self) {}
}
#[cfg(test)]
pub mod testing {
use super::{Answer, Channel, Choice, Question, Report};
use std::sync::Mutex;
pub struct Silent;
impl Channel for Silent {
fn report(&self, _report: &Report) {}
fn ask(&self, _question: &Question) {}
fn withdraw(&self, _about: &str) {}
fn answers(&self) -> Vec<Answer> {
Vec::new()
}
}
#[derive(Default)]
pub struct Recording {
reports: Mutex<Vec<Report>>,
questions: Mutex<Vec<Question>>,
withdrawn: Mutex<Vec<String>>,
pending: Mutex<Vec<Answer>>,
insisted: Mutex<Vec<Report>>,
}
impl Recording {
#[must_use]
pub fn insisted(&self) -> Vec<Report> {
self.insisted.lock().unwrap().clone()
}
#[must_use]
pub fn reports(&self) -> Vec<Report> {
self.reports.lock().unwrap().clone()
}
#[must_use]
pub fn said(&self) -> String {
self.reports
.lock()
.unwrap()
.iter()
.fold(String::new(), |mut all, r| {
all.push_str(&r.summary);
all.push('\n');
all.push_str(&r.detail.join("\n"));
all.push('\n');
all
})
}
#[must_use]
pub fn questions(&self) -> Vec<Question> {
self.questions.lock().unwrap().clone()
}
#[must_use]
pub fn withdrawn(&self) -> Vec<String> {
self.withdrawn.lock().unwrap().clone()
}
pub fn answer(&self, about: &str, choice: Choice) {
self.pending.lock().unwrap().push(Answer {
about: about.to_owned(),
choice,
});
}
}
impl Channel for Recording {
fn report(&self, report: &Report) {
self.reports.lock().unwrap().push(report.clone());
}
fn ask(&self, question: &Question) {
self.questions.lock().unwrap().push(question.clone());
}
fn withdraw(&self, about: &str) {
self.withdrawn.lock().unwrap().push(about.to_owned());
}
fn answers(&self) -> Vec<Answer> {
std::mem::take(&mut *self.pending.lock().unwrap())
}
fn insist(&self, report: &Report) {
self.insisted.lock().unwrap().push(report.clone());
}
}
}