scraper_trail/archive/
store.rs1use 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 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}