walker_common/
changes.rs

1//! Changes based on the `changes.csv` file.
2
3use 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/// An entry when a resource was last changed.
18#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
19pub struct ChangeEntry {
20    /// The relative file name
21    pub file: String,
22    /// The timestamp of the last change
23    #[serde(with = "time::serde::iso8601")]
24    pub timestamp: OffsetDateTime,
25}
26
27/// State of a `changes.csv` file.
28pub struct ChangeSource {
29    pub entries: Vec<ChangeEntry>,
30}
31
32impl ChangeSource {
33    /// Retrieve a file using a [`Fetcher`].
34    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}