Skip to main content

scraper_trail/archive/
store.rs

1use std::{
2    marker::PhantomData,
3    path::{Path, PathBuf},
4};
5
6use crate::archive::Archiveable;
7
8#[derive(Debug, thiserror::Error)]
9pub enum Error {
10    #[error("I/O error")]
11    Io(#[from] std::io::Error),
12    #[error("JSON error")]
13    Json(#[from] serde_json::Error),
14}
15
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct Store {
18    pub base: PathBuf,
19}
20
21impl Store {
22    pub fn new<P: AsRef<Path>>(base: P) -> Self {
23        Self {
24            base: base.as_ref().to_path_buf(),
25        }
26    }
27
28    pub fn paths(&self, reverse: bool) -> Result<Vec<PathBuf>, std::io::Error> {
29        let mut paths = std::fs::read_dir(&self.base)?
30            .map(|entry| entry.map(|entry| entry.path()))
31            .collect::<Result<Vec<_>, _>>()?;
32
33        paths.sort();
34
35        if reverse {
36            paths.reverse();
37        }
38
39        Ok(paths)
40    }
41
42    pub fn contents(&self, reverse: bool) -> Result<Contents, std::io::Error> {
43        Ok(Contents {
44            // We put the paths in reverse order, since we'll be popping them off the `Vec`.
45            paths: self.paths(!reverse)?,
46        })
47    }
48
49    pub fn entries<T>(&self, reverse: bool) -> Result<Entries<T>, std::io::Error> {
50        Ok(Entries {
51            contents: self.contents(reverse)?,
52            _target: PhantomData,
53        })
54    }
55}
56
57#[derive(Clone, Debug, Eq, PartialEq)]
58pub struct Contents {
59    paths: Vec<PathBuf>,
60}
61
62impl Iterator for Contents {
63    type Item = (PathBuf, Result<String, std::io::Error>);
64
65    fn next(&mut self) -> Option<Self::Item> {
66        self.paths.pop().map(|path| {
67            let contents = std::fs::read_to_string(&path);
68
69            (path, contents)
70        })
71    }
72}
73
74#[derive(Clone, Debug, Eq, PartialEq)]
75pub struct Entries<T> {
76    contents: Contents,
77    _target: PhantomData<T>,
78}
79
80use bounded_static::IntoBoundedStatic;
81
82impl<T: Archiveable + IntoBoundedStatic> Iterator for Entries<T>
83where
84    T::Static: Archiveable,
85    T::RequestParams: Into<<T::Static as Archiveable>::RequestParams>,
86{
87    type Item = (
88        PathBuf,
89        Result<crate::archive::entry::Entry<'static, T::Static>, Error>,
90    );
91
92    fn next(&mut self) -> Option<Self::Item> {
93        self.contents.next().map(|(path, contents)| {
94            let entry = contents.map_err(Error::from).and_then(|contents| {
95                serde_json::from_str::<crate::archive::entry::Entry<'_, T>>(&contents)
96                    .map(bounded_static::IntoBoundedStatic::into_static)
97                    .map_err(Error::from)
98            });
99
100            (path, entry)
101        })
102    }
103}