Skip to main content

jobberdb/
configuration.rs

1//! Configuration of a *jobber* database.
2
3use crate::prelude::*;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7/// Configuration of a *jobber* database.
8#[derive(Serialize, Deserialize, Debug, Clone, Default)]
9pub struct Configuration {
10    /// Configuration used when no tag related configuration fit
11    pub base: Properties,
12    /// Configuration by tag
13    pub tags: HashMap<String, Properties>,
14}
15
16impl Configuration {
17    /// Partially overwrite properties of configurations which match the given tags.
18    pub fn set(&mut self, tags: &Option<TagSet>, update: &Properties) -> bool {
19        let mut modified = false;
20        if let Some(tags) = tags {
21            for tag in tags.iter() {
22                if let Some(tag_configuration) = self.tags.get_mut(tag) {
23                    if tag_configuration.update(update.clone()) {
24                        modified = true;
25                    }
26                } else {
27                    self.tags.insert(tag.clone(), update.clone());
28                    modified = true;
29                }
30            }
31        } else if self.base.update(update.clone()) {
32            modified = true;
33        }
34        modified
35    }
36    /// get properties for the given tags and additionally return which tag was relevant
37    pub fn get_and_why(&self, tags: &TagSet) -> (Option<String>, &Properties) {
38        for tag in &tags.0 {
39            if let Some(properties) = self.tags.get(tag) {
40                return (Some(tag.clone()), properties);
41            }
42        }
43        (None, &self.base)
44    }
45    /// get properties for the given tags
46    pub fn get(&self, tags: &TagSet) -> &Properties {
47        match &self.get_checked(tags) {
48            Ok(properties) => properties,
49            _ => panic!("unexpected tag collision"),
50        }
51    }
52    /// get properties for the given tags and also check tag configuration consistency
53    pub fn get_checked(&self, tags: &TagSet) -> Result<&Properties, Error> {
54        let mut found = TagSet::new();
55        let mut properties = None;
56        for tag in &tags.0 {
57            if let Some(p) = self.tags.get(tag) {
58                found.insert(tag);
59                properties = Some(p);
60            }
61        }
62        match found.len() {
63            0 => Ok(&self.base),
64            1 => Ok(properties.unwrap()),
65            _ => Err(Error::TagCollision(found)),
66        }
67    }
68}
69
70/// Properties within the database configuration.
71#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
72pub struct Properties {
73    /// Time resolution in fractional hours
74    pub resolution: Option<f64>,
75    /// Rate for an hour
76    pub rate: Option<f64>,
77    /// Maximum work hours per day
78    pub max_hours: Option<u32>,
79}
80
81impl Properties {
82    /// Update properties.
83    /// # Arguments
84    /// - `properties`: Properties to overwrite (empty properties will be ignored)
85    /// # Return Value
86    /// Returns `true` if any modification was made.
87    pub fn update(&mut self, properties: Properties) -> bool {
88        let mut modified = false;
89        if let Some(resolution) = properties.resolution {
90            self.resolution = Some(resolution);
91            modified = true;
92        }
93        if let Some(rate) = properties.rate {
94            self.rate = Some(rate);
95            modified = true;
96        }
97        if let Some(max_hours) = properties.max_hours {
98            self.max_hours = Some(max_hours);
99            modified = true;
100        }
101        modified
102    }
103}
104
105impl Default for Properties {
106    fn default() -> Self {
107        Self {
108            resolution: Some(0.25),
109            rate: None,
110            max_hours: None,
111        }
112    }
113}
114
115impl std::fmt::Display for Properties {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        if let Some(resolution) = self.resolution {
118            writeln!(f, "Resolution: {} hours", resolution)?;
119        }
120        if let Some(rate) = self.rate {
121            writeln!(f, "Payment per hour: {}", rate)?
122        };
123        if let Some(max_hours) = self.max_hours {
124            writeln!(f, "Maximum work time: {} hours", max_hours)?
125        };
126        Ok(())
127    }
128}