1use std::fs::File;
3use std::io::{BufReader, BufWriter, ErrorKind, Read, Write};
4use std::ops::Deref;
5use std::path::{Path, PathBuf};
6use std::time::{Duration, SystemTime};
7
8#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
9pub struct SinceState {
10 pub last_run: SystemTime,
11}
12
13impl SinceState {
14 pub fn load<R>(reader: R) -> anyhow::Result<Self>
16 where
17 R: Read,
18 {
19 Ok(serde_json::from_reader(reader)?)
20 }
21
22 pub fn load_from<F>(file: F) -> anyhow::Result<Option<Self>>
24 where
25 F: AsRef<Path>,
26 {
27 match File::open(file) {
28 Ok(file) => Self::load(BufReader::new(file)).map(Option::Some),
29 Err(err) if err.kind() == ErrorKind::NotFound => Ok(None),
30 Err(err) => Err(err.into()),
31 }
32 }
33
34 pub fn store<W>(&self, writer: W) -> anyhow::Result<()>
36 where
37 W: Write,
38 {
39 Ok(serde_json::to_writer(writer, &self)?)
40 }
41}
42
43pub struct Since {
45 pub since: Option<SystemTime>,
46 pub last_run: SystemTime,
47 pub since_file: Option<PathBuf>,
48}
49
50impl Deref for Since {
51 type Target = Option<SystemTime>;
52
53 fn deref(&self) -> &Self::Target {
54 &self.since
55 }
56}
57
58impl Since {
59 pub fn new(
60 since: Option<impl Into<SystemTime>>,
61 since_file: Option<PathBuf>,
62 since_file_offset: Duration,
63 ) -> anyhow::Result<Self> {
64 let since = match (since, &since_file) {
65 (skip, Some(file)) => match SinceState::load_from(file)? {
67 Some(since) => {
68 let result = since.last_run + since_file_offset;
69 log::info!(
70 "Since state from file - last run: {}, offset: {} = {}",
71 humantime::Timestamp::from(since.last_run),
72 humantime::Duration::from(since_file_offset),
73 humantime::Timestamp::from(result),
74 );
75 Some(result)
76 }
77 None => skip.map(|s| s.into()),
78 },
79 (Some(skip), None) => {
81 let since = skip.into();
82 log::info!("Using provided since {}", humantime::Timestamp::from(since));
83 Some(since)
84 }
85 (None, None) => None,
87 };
88
89 let last_run = SystemTime::now();
90
91 Ok(Since {
92 since,
93 last_run,
94 since_file,
95 })
96 }
97
98 pub fn store(self) -> anyhow::Result<()> {
99 if let Some(path) = &self.since_file {
100 log::info!(
101 "Storing last_run = {}",
102 humantime::Timestamp::from(self.last_run)
103 );
104 SinceState {
105 last_run: self.last_run,
106 }
107 .store(BufWriter::new(File::create(path)?))?;
108 }
109 Ok(())
110 }
111}