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
use crate::models::error::{ModelError, ModelErrorKind};
use crate::models::PluginID;
use chrono::{DateTime, Duration, Utc};
use failure::ResultExt;
use serde_derive::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};
static SYNC_AMOUNT_DEFAULT: u32 = 300;
static CONFIG_NAME: &str = "newsflash.json";
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
backend: Option<String>,
sync_amount: u32,
#[serde(with = "json_time")]
last_sync: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
keep_articles_days: Option<u64>,
#[serde(skip_serializing)]
#[serde(skip_deserializing)]
path: PathBuf,
}
impl Config {
pub fn open(path: &Path) -> Result<Self, ModelError> {
let path = path.join(CONFIG_NAME);
if path.as_path().exists() {
let data = fs::read_to_string(&path).context(ModelErrorKind::IO)?;
let mut config: Self = serde_json::from_str(&data).context(ModelErrorKind::Json)?;
config.path = path;
return Ok(config);
}
let config = Config {
backend: None,
sync_amount: SYNC_AMOUNT_DEFAULT,
last_sync: Utc::now() - Duration::weeks(1),
keep_articles_days: None,
path,
};
config.write()?;
Ok(config)
}
fn write(&self) -> Result<(), ModelError> {
let data = serde_json::to_string_pretty(self).context(ModelErrorKind::Json)?;
fs::write(&self.path, data).context(ModelErrorKind::IO)?;
Ok(())
}
pub fn get_backend(&self) -> Option<PluginID> {
self.backend.as_ref().map(|plugin_id| PluginID::new(plugin_id))
}
pub fn set_backend(&mut self, backend: Option<&PluginID>) -> Result<(), ModelError> {
self.backend = backend.map(|plugin_id| plugin_id.as_str().to_owned());
self.write()?;
Ok(())
}
pub fn get_sync_amount(&self) -> u32 {
self.sync_amount
}
pub fn set_sync_amount(&mut self, amount: u32) -> Result<(), ModelError> {
self.sync_amount = amount;
self.write()?;
Ok(())
}
pub fn get_last_sync(&self) -> DateTime<Utc> {
self.last_sync
}
pub fn set_last_sync(&mut self, time: DateTime<Utc>) -> Result<(), ModelError> {
self.last_sync = time;
self.write()?;
Ok(())
}
pub fn get_keep_articles_duration(&self) -> Option<Duration> {
self.keep_articles_days.map(|days| Duration::days(days as i64))
}
pub fn set_keep_articles_duration(&mut self, duration: Option<Duration>) -> Result<(), ModelError> {
if let Some(duration) = duration {
if duration < Duration::days(1) {
log::error!("Duration to keep articles is not allowed to be < 1 Day. Duration: {}", duration);
return Err(ModelErrorKind::Input.into());
}
}
self.keep_articles_days = duration.map(|d| d.num_days() as u64);
self.write()?;
Ok(())
}
}
mod json_time {
use chrono::{DateTime, Utc};
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
pub fn time_to_json(t: DateTime<Utc>) -> String {
t.to_rfc3339()
}
pub fn serialize<S: Serializer>(time: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> {
time_to_json(*time).serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<DateTime<Utc>, D::Error> {
let time: String = Deserialize::deserialize(deserializer)?;
Ok(DateTime::parse_from_rfc3339(&time).map_err(D::Error::custom)?.with_timezone(&Utc))
}
}