1use crate::fetcher::{self, Fetcher};
4use time::OffsetDateTime;
5use url::{ParseError, Url};
6
7#[derive(Debug, thiserror::Error)]
8pub enum Error {
9 #[error("Fetch error: {0}")]
10 Fetcher(#[from] fetcher::Error),
11 #[error("URL error: {0}")]
12 Url(#[from] ParseError),
13 #[error("CSV error: {0}")]
14 Csv(#[from] csv::Error),
15}
16
17#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
19pub struct ChangeEntry {
20 pub file: String,
22 #[serde(with = "time::serde::iso8601")]
24 pub timestamp: OffsetDateTime,
25}
26
27pub struct ChangeSource {
29 pub entries: Vec<ChangeEntry>,
30}
31
32impl ChangeSource {
33 pub async fn retrieve(fetcher: &Fetcher, base_url: &Url) -> Result<Self, Error> {
35 let changes = fetcher
36 .fetch::<String>(base_url.join("changes.csv")?)
37 .await?;
38
39 log::info!("Found 'changes.csv', processing data");
40
41 let reader = csv::ReaderBuilder::new()
42 .delimiter(b',')
43 .has_headers(false)
44 .from_reader(changes.as_bytes());
45
46 let entries = reader
47 .into_deserialize::<ChangeEntry>()
48 .collect::<Result<Vec<_>, _>>()?;
49
50 log::info!("Detected {} entries", entries.len());
51
52 Ok(Self { entries })
53 }
54}