use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct TaskWorkLog {
pub ended_at: Option<DateTime<Utc>>,
pub note: Option<String>,
pub source: Option<String>,
pub started_at: DateTime<Utc>,
}
impl TaskWorkLog {
pub fn new() -> Self {
Self {
ended_at: None,
note: None,
source: None,
started_at: Utc::now(),
}
}
pub fn add_note(&mut self, note: impl Into<String>) {
self.note = Some(note.into());
}
pub fn add_source(&mut self, source: impl Into<String>) {
self.source = Some(source.into());
}
pub fn duration(&self) -> Option<Duration> {
self.ended_at.map(|ended_at| ended_at - self.started_at)
}
pub fn stop(&mut self) {
if self.ended_at.is_none() {
self.ended_at = Some(Utc::now());
}
}
#[must_use]
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.note = Some(note.into());
self
}
#[must_use]
pub fn with_source(mut self, source: impl Into<String>) -> Self {
self.source = Some(source.into());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
mod new {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_creates_a_new_work_log() {
let work_log = TaskWorkLog::new();
assert_eq!(work_log.ended_at, None);
assert_eq!(work_log.note, None);
assert_eq!(work_log.source, None);
assert!(work_log.started_at.timestamp() > 0);
}
}
mod add_note {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_adds_a_note_to_the_worklog() {
let mut work_log = TaskWorkLog::new();
work_log.add_note("a test note");
assert_eq!(work_log.note.unwrap(), "a test note");
}
}
mod add_source {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_adds_a_source_to_the_worklog() {
let mut work_log = TaskWorkLog::new();
work_log.add_source("git");
assert_eq!(work_log.source.unwrap(), "git");
}
}
mod duration {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_returns_the_duration_of_the_work_log() {
let now = Utc::now();
let mut work_log = TaskWorkLog::new();
work_log.started_at = now - Duration::minutes(10);
work_log.ended_at = Some(now);
assert_eq!(work_log.duration().unwrap(), Duration::minutes(10));
}
#[test]
fn it_returns_none_if_the_work_log_has_not_been_ended() {
let work_log = TaskWorkLog::new();
assert_eq!(work_log.duration(), None);
}
}
mod stop {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_sets_the_ended_at_to_now() {
let mut work_log = TaskWorkLog::new();
work_log.stop();
assert!(work_log.ended_at.is_some());
}
#[test]
fn it_does_not_set_the_ended_at_if_it_has_already_been_set() {
let mut work_log = TaskWorkLog::new();
let ended_at = Utc::now() - Duration::minutes(10);
work_log.ended_at = Some(ended_at);
work_log.stop();
assert_eq!(work_log.ended_at.unwrap(), ended_at);
}
}
mod with_note {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_creates_a_new_work_log_with_the_given_note() {
let work_log = TaskWorkLog::new().with_note("a test note");
assert_eq!(work_log.note.unwrap(), "a test note");
}
}
mod with_source {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_creates_a_new_work_log_with_the_given_source() {
let work_log = TaskWorkLog::new().with_source("git");
assert_eq!(work_log.source.unwrap(), "git");
}
}
}