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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use serde_yaml;
use ordered_float::OrderedFloat;
use num_traits::Float;
use std::collections::HashMap;

use super::error::Result;

#[derive(Debug, Serialize, Deserialize)]
#[serde(remote = "OrderedFloat")]
pub struct OrderedFloatDef<T: Float>(pub T);

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
pub struct Number(
    #[serde(with = "OrderedFloatDef")]
    OrderedFloat<f64>
    );

impl From<Number> for f64 {
    fn from (num: Number) -> f64 {
        let Number(ord) = num;
        ord.into_inner()
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Project {
    client: Client,
    event: Event,
    hours: Hours,
    products: HashMap<ProductDesc, Product>,
    manager: Option<String>,
    invoicer_version: Option<String>,
    template: Option<String>,
    created: Option<String>,
    canceled: Option<String>,
    meta: Option<Meta>,
    tax: Option<Number>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Client {
    title: String,
    first_name: String,
    last_name: String,
    email: Option<String>,
    address: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Event {
    name: String,
    location: Option<String>,
    description: Option<String>,
    dates: Vec<ProjectDate>
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "Date")]
pub struct ProjectDate {
    begin: String,
    end: Option<String>,
    times: Vec<Time>
}

#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ProductDesc {
    Name(String),
    Head{
        name: String,
        price: Number,
        unit: Option<String>,
        tax: Option<Number>
    },

}

impl ProductDesc {
    pub fn name(&self) -> String {
        match *self {
            ProductDesc::Name(ref name) => name.to_owned(),
            ProductDesc::Head{ref name, .. } => name.clone(),

        }
    }

    pub fn price(&self) -> Option<f64> {
        match *self {
            ProductDesc::Name(_) => None,
            ProductDesc::Head{ref price, .. } => Some(price.clone().into()),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Product {
    amount: f64,
    returned: Option<f64>,
    price: Option<f64>,
    sold: Option<f64>,
}


#[derive(Debug, Serialize, Deserialize)]
pub struct Hours {
    salary: f64,
    caterers: HashMap<String, f64>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Time {
    begin: String,
    end: Option<String>,
}


#[derive(Debug, Serialize, Deserialize)]
pub struct Meta {
    invoicer_version: String,
    template: String,
    format: String,
}

pub fn from_str(content: &str) -> Result<Project> {
    Ok(serde_yaml::from_str(content)?)
}