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
use cache::{Cache, NoopCache};
use chrono::{Duration, NaiveDate};
use manifest::Manifest;
use source::{DefaultSource, SourceInfo};
use std::{io, iter};
use Error;
use {reqwest, toml};
pub struct Downloader<S, C = NoopCache> {
client: reqwest::Client,
source: S,
cache: C,
}
impl<'a> Downloader<DefaultSource<'a>> {
pub fn with_default_source(channel: &'a str) -> Self {
Self::new(DefaultSource::new(channel))
}
}
impl<S> Downloader<S> {
pub fn new(source: S) -> Self {
Downloader {
client: reqwest::Client::new(),
source,
cache: NoopCache {},
}
}
}
impl<S, C> Downloader<S, C>
where
S: SourceInfo,
C: Cache,
{
pub fn set_cache<NewCache: Cache>(self, c: NewCache) -> Downloader<S, NewCache> {
Downloader {
client: self.client,
source: self.source,
cache: c,
}
}
pub fn get_last_manifests(&self, days: usize) -> Result<Vec<Manifest>, Error> {
let latest = self.get_latest_manifest()?;
let latest_day = latest.date;
info!("Latest manifest is for {}", latest_day);
let rest = (1..days)
.filter_map(|day| latest_day.checked_sub_signed(Duration::days(day as i64)))
.map(|date| self.get_manifest(date));
iter::once(Ok(latest)).chain(rest).collect()
}
pub fn get_manifest(&self, day: NaiveDate) -> Result<Manifest, Error> {
if let Some(cached) = self.cache.get(day) {
return Ok(cached);
}
let manifest = self.get_manifest_by_url(self.source.make_manifest_url(day))?;
self.cache.store(&manifest);
Ok(manifest)
}
pub fn get_latest_manifest(&self) -> Result<Manifest, Error> {
self.get_manifest_by_url(self.source.make_latest_manifest_url())
}
pub fn get_manifest_by_url(&self, url: impl AsRef<str>) -> Result<Manifest, Error> {
let url = url.as_ref();
info!("Fetching a manifest from {}", url);
let mut response = self
.client
.get(url)
.send()
.map_err(|e| Error::Reqwest(e, url.into()))?;
if !response.status().is_success() {
return Err(Error::BadResponse(response.status(), url.into()));
}
let mut bytes = Vec::new();
io::copy(&mut response, &mut bytes).map_err(|e| Error::Io(e, url.into()))?;
toml::from_slice(&bytes).map_err(|e| (e, url.to_string()).into())
}
}