1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
/* Copyright © 2021 Randy Barlow
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
//! # The Ui Trait
//!
//! The Ui Trait defines an interface for bridging human interactions with the rpick crate.

#[cfg(test)]
use mockall::automock;

/// An individual cell within rpick's chance tables.
///
/// Each of the variants expresses its contained type, and should be fairly obvious.
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum Cell<'a> {
    Boolean(bool),
    Text(&'a str),
    Integer(i64),
    Float(f64),
    Unsigned(u64),
}

impl<'a> From<f64> for Cell<'_> {
    fn from(f: f64) -> Self {
        Self::Float(f)
    }
}

impl<'a> From<&'a str> for Cell<'a> {
    fn from(s: &'a str) -> Self {
        Self::Text(s)
    }
}

impl<'a> From<u64> for Cell<'_> {
    fn from(u: u64) -> Self {
        Self::Unsigned(u)
    }
}

impl<'a> From<&Cell<'_>> for String {
    fn from(c: &Cell) -> String {
        match c {
            Cell::Boolean(value) => value.to_string(),
            Cell::Text(value) => value.to_string(),
            Cell::Integer(value) => value.to_string(),
            Cell::Float(value) => value.to_string(),
            Cell::Unsigned(value) => value.to_string(),
        }
    }
}

/// Represents a row in the [`Table`] struct.
#[derive(Debug, PartialEq)]
pub struct Row<'a> {
    /// The row's individual cells.
    pub cells: Vec<Cell<'a>>,
    /// Whether this row was chosen in a rpick.
    pub chosen: bool,
}

/// rpick uses this to send a chance table to the user.
#[derive(Debug, PartialEq)]
pub struct Table<'a> {
    /// The Table's footer.
    pub footer: Vec<Cell<'a>>,
    /// The Table's header.
    pub header: Vec<Cell<'a>>,
    /// The Table's rows.
    pub rows: Vec<Row<'a>>,
}

/// A struct implementing this trait must be passed to the rpick engine.
///
/// This is how rpick interacts with users.
#[cfg_attr(test, automock)]
pub trait Ui {
    /// If this method returns `true`, [`Ui::display_table`] will be called by the engine.
    ///
    /// This is a small optimization - generating tables that the Ui isn't going to show to the
    /// user or otherwise use is a waste of compute time. If the table isn't going to get used,
    /// this method should return `false`.
    fn call_display_table(&self) -> bool;

    /// Display the given table to the user.
    fn display_table<'a>(&self, table: &Table<'a>);

    /// Display the given message to the user.
    fn info(&self, message: &str);

    /// Prompt the user if they wish to accept the given choice.
    ///
    /// Return `true` if the user accepts the choice.
    fn prompt_choice(&self, choice: &str) -> bool;
}