Lines
13.49 %
Functions
11.11 %
Branches
100 %
// This file is part of hnefatafl-copenhagen.
//
// hnefatafl-copenhagen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// hnefatafl-copenhagen 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 Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::fmt;
use anyhow::Context;
use serde::{Deserialize, Serialize};
pub const DAY: i64 = 24 * 60 * 60 * 1_000;
pub const HOUR: i64 = 60 * 60 * 1_000;
pub const MINUTE: i64 = 60 * 1_000;
pub const SECOND: i64 = 1_000;
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Time {
pub add_seconds: i64,
pub milliseconds_left: i64,
}
impl Time {
#[must_use]
pub fn time_left(&self) -> String {
time_left(self.milliseconds_left)
impl Default for Time {
fn default() -> Self {
Self {
add_seconds: 10,
milliseconds_left: 15 * MINUTE,
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let days = self.milliseconds_left / DAY;
let hours = (self.milliseconds_left % DAY) / HOUR;
let minutes = (self.milliseconds_left % HOUR) / MINUTE;
let seconds = (self.milliseconds_left % MINUTE) / SECOND;
let add_hours = self.add_seconds / (60 * 60);
let add_minutes = (self.add_seconds % (60 * 60)) / 60;
let add_seconds = self.add_seconds % 60;
if days == 0 {
write!(
f,
"{hours:02}:{minutes:02}:{seconds:02} | {add_hours:02}:{add_minutes:02}:{add_seconds:02}"
)
} else {
"{days} {hours:02}:{minutes:02}:{seconds:02} | {add_hours:02}:{add_minutes:02}:{add_seconds:02}"
#[derive(Clone, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum TimeSettings {
Timed(Time),
UnTimed,
impl TimeSettings {
match self {
Self::Timed(time) => time.time_left(),
Self::UnTimed => "-".to_string(),
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct TimeLeft {
impl fmt::Display for TimeLeft {
write!(f, "{}", time_left(self.milliseconds_left))
impl From<Time> for TimeLeft {
fn from(time: Time) -> Self {
TimeLeft {
milliseconds_left: time.milliseconds_left,
impl TryFrom<TimeSettings> for TimeLeft {
type Error = anyhow::Error;
fn try_from(time: TimeSettings) -> Result<TimeLeft, anyhow::Error> {
match time {
TimeSettings::Timed(time) => Ok(TimeLeft {
}),
TimeSettings::UnTimed => Err(anyhow::Error::msg("the time settings are un-timed")),
impl Default for TimeSettings {
Self::Timed(Time {
..Default::default()
})
impl fmt::Debug for TimeSettings {
Self::Timed(time) => {
write!(f, "fischer {} {}", time.milliseconds_left, time.add_seconds)
Self::UnTimed => write!(f, "un-timed _ _"),
impl fmt::Display for TimeSettings {
Self::Timed(time) => write!(f, "{time}"),
Self::UnTimed => write!(f, "∞"),
impl From<TimeSettings> for bool {
fn from(time_settings: TimeSettings) -> Self {
match time_settings {
TimeSettings::Timed(_) => true,
TimeSettings::UnTimed => false,
impl TryFrom<Vec<&str>> for TimeSettings {
fn try_from(args: Vec<&str>) -> anyhow::Result<Self> {
let err_msg = "expected: 'time_settings un-timed' or 'time_settings fischer MILLISECONDS ADD_SECONDS'";
if Some("un-timed").as_ref() == args.get(1) {
return Ok(Self::UnTimed);
if args.len() < 4 {
return Err(anyhow::Error::msg(err_msg));
if "fischer" == args[1] {
let arg_2 = args[2]
.parse::<i64>()
.context("time_settings: arg 2 is not an integer")?;
let arg_3 = args[3]
.context("time_settings: arg 3 is not an integer")?;
Ok(Self::Timed(Time {
add_seconds: arg_3,
milliseconds_left: arg_2,
}))
Err(anyhow::Error::msg(err_msg))
fn time_left(milliseconds_left: i64) -> String {
let days = milliseconds_left / DAY;
let hours = (milliseconds_left % DAY) / HOUR;
let minutes = (milliseconds_left % HOUR) / MINUTE;
let seconds = (milliseconds_left % MINUTE) / SECOND;
if hours == 0 {
if minutes == 0 {
format!("{seconds:02}")
format!("{minutes:02}:{seconds:02}")
format!("{hours:02}:{minutes:02}:{seconds:02}")
format!("{days} {hours:02}:{minutes:02}:{seconds:02}")
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum TimeEnum {
Classical,
Infinity,
Long,
#[default]
Rapid,
VeryLong,
impl From<TimeEnum> for TimeSettings {
fn from(time_enum: TimeEnum) -> TimeSettings {
match time_enum {
TimeEnum::Classical => TimeSettings::Timed(Time {
add_seconds: 20,
milliseconds_left: 30 * MINUTE,
TimeEnum::Rapid => TimeSettings::Timed(Time {
TimeEnum::Long => TimeSettings::Timed(Time {
add_seconds: 60 * 60 * 6,
milliseconds_left: 3 * DAY,
TimeEnum::VeryLong => TimeSettings::Timed(Time {
add_seconds: 60 * 60 * 15,
milliseconds_left: 12 * 15 * HOUR,
TimeEnum::Infinity => TimeSettings::UnTimed,
impl fmt::Display for TimeEnum {
Self::Classical => write!(f, "00:30:00 | 00:00:20"),
Self::Infinity => write!(f, "∞"),
Self::Long => write!(f, "3 00:00:00 | 6:00:00"),
Self::Rapid => write!(f, "00:15:00 | 00:00:10 "),
Self::VeryLong => write!(f, "7 12:00:00 | 15:00:00 "),