1use anyhow::{Context, Result};
6use chrono::Utc;
7use std::fs;
8use tracing::{debug, info};
9
10use crate::mattermost::{BaseSession, MMStatus};
11use serde::{Deserialize, Serialize};
12use std::path::PathBuf;
13
14const MAX_SECS_BEFORE_FORCE_UPDATE: u64 = 60 * 60;
18
19#[derive(Debug)]
21pub struct Cache {
22 path: PathBuf,
23}
24
25impl Cache {
26 pub fn new(path: impl Into<PathBuf>) -> Self {
28 Self { path: path.into() }
29 }
30}
31
32#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
34pub enum Location {
35 Known(String),
37 Unknown,
39}
40
41#[derive(Serialize, Deserialize, Debug)]
43pub struct State {
44 location: Location,
45 timestamp: i64,
46}
47
48impl State {
49 pub fn new(cache: &Cache) -> Result<Self> {
52 let res: State;
53 if let Ok(json) = &fs::read(&cache.path) {
54 res = serde_json::from_str(&String::from_utf8_lossy(json)).context(format!(
55 "Unable to deserialize state file {:?} (try to remove it)",
56 &cache.path
57 ))?;
58 } else {
59 res = Self {
60 location: Location::Unknown,
61 timestamp: 0,
62 };
63 }
64 debug!("Previous known location `{:?}`", res.location);
65 Ok(res)
66 }
67
68 pub fn set_location(&mut self, location: Location, cache: &Cache) -> Result<()> {
70 info!("Set location to `{:?}`", location);
71 self.location = location;
72 self.timestamp = Utc::now().timestamp();
73 fs::write(
74 &cache.path,
75 serde_json::to_string(&self)
76 .unwrap_or_else(|_| panic!("Serialization of State Failed :{:?}", &self)),
77 )
78 .with_context(|| format!("Writing to cache file {:?}", cache.path))?;
79 Ok(())
80 }
81
82 pub fn update_status(
89 &mut self,
90 current_location: Location,
91 status: Option<&mut MMStatus>,
92 session: &mut Box<dyn BaseSession>,
93 cache: &Cache,
94 delay_between_polling: u64,
95 ) -> Result<()> {
96 if current_location == Location::Unknown {
97 return Ok(());
98 } else if current_location == self.location {
99 let elapsed_sec: u64 = (Utc::now().timestamp() - self.timestamp)
102 .try_into()
103 .unwrap();
104 if delay_between_polling * 2 < elapsed_sec
105 && elapsed_sec <= MAX_SECS_BEFORE_FORCE_UPDATE
106 {
107 debug!(
108 "No change for {}s : no update to mattermost status",
109 MAX_SECS_BEFORE_FORCE_UPDATE
110 );
111 return Ok(());
112 }
113 }
114 status.unwrap().send(session)?;
116 self.set_location(current_location, cache)?;
118 Ok(())
119 }
120}
121
122#[cfg(test)]
123mod should {
124 use super::*;
125 use mktemp::Temp;
126 use test_log::test; #[test]
128 fn remember_state() -> Result<()> {
129 let temp = Temp::new_file().unwrap().to_path_buf();
130 let cache = Cache::new(&temp);
131 let mut state = State::new(&cache)?;
132 assert_eq!(state.location, Location::Unknown);
133 state.set_location(Location::Known("abcd".to_string()), &cache)?;
134 assert_eq!(state.location, Location::Known("abcd".to_string()));
135 let mut state = State::new(&cache)?;
136 assert_eq!(state.location, Location::Known("abcd".to_string()));
137 state.set_location(Location::Known("work".to_string()), &cache)?;
138 assert_eq!(state.location, Location::Known("work".to_string()));
139 Ok(())
140 }
141}