use crate::{allocation::Allocation, traits::Contribution};
use chrono::{prelude::*, Duration};
#[derive(PartialEq, Debug)]
pub enum ProjectBuilderError {
ZeroLengthDuration,
}
impl std::error::Error for ProjectBuilderError {}
impl std::fmt::Display for ProjectBuilderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
ProjectBuilderError::ZeroLengthDuration => write!(f, "Project has no duration."),
}
}
}
#[derive(PartialEq, Debug)]
pub struct ProjectBuilder {
allocation: Allocation,
name: String,
value: u32,
}
impl Default for ProjectBuilder {
fn default() -> Self {
ProjectBuilder {
allocation: Allocation::default(),
name: "New Project".into(),
value: 20000,
}
}
}
impl ProjectBuilder {
pub fn start_date(mut self, date: &Date<Utc>) -> ProjectBuilder {
let duration = self.allocation.duration();
self.allocation = Allocation {
start_date: *date,
end_date: *date + duration,
};
self
}
pub fn value(mut self, value: u32) -> ProjectBuilder {
self.value = value;
self
}
pub fn name(mut self, name: &str) -> ProjectBuilder {
self.name = String::from(name);
self
}
pub fn duration_weeks(self, num_of_weeks: i64) -> ProjectBuilder {
self.duration(&Duration::weeks(num_of_weeks))
}
pub fn duration(mut self, duration: &Duration) -> ProjectBuilder {
let start_date = self.allocation.start_date;
self.allocation = Allocation {
start_date,
end_date: start_date + *duration,
};
self
}
pub fn build(self) -> Project {
Project {
allocation: self.allocation,
approx_value: self.value,
name: self.name,
}
}
}
#[derive(PartialEq, Debug)]
pub struct Project {
allocation: Allocation,
pub name: String,
approx_value: u32,
}
impl Default for Project {
fn default() -> Self {
Project {
allocation: Allocation::default(),
approx_value: 20000,
name: "New Project".into(),
}
}
}
impl std::fmt::Display for Project {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} ({}) {}", self.name, self.allocation, self.value())
}
}
impl Project {
pub fn duration(&self) -> Duration {
self.allocation.duration()
}
pub fn value(&self) -> u32 {
self.approx_value
}
pub fn allocation(&self) -> Allocation {
self.allocation
}
}
impl Contribution for Project {
fn get_contribution_on(&self, date: &Date<Utc>) -> u32 {
match self.allocation.is_active_on(date) {
true => self.approx_value,
false => 0_u32,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_duration() {
let p = Project::default();
assert_eq!(p.duration(), Duration::weeks(4))
}
#[test]
fn default_builder_duration() {
let p = ProjectBuilder::default().build();
assert_eq!(p.duration(), Duration::weeks(4))
}
#[test]
fn builder_set_date_duration() {
let p = ProjectBuilder::default()
.start_date(&Utc.ymd(2014, 7, 10))
.build();
assert_eq!(p.duration(), Duration::weeks(4))
}
#[test]
fn dynamic_date() {
let new_date = Utc.ymd(2014, 7, 10) + Duration::weeks(2);
let p = ProjectBuilder::default().start_date(&new_date).build();
assert_eq!(p.duration(), Duration::weeks(4))
}
}