mocklogger/
timefail.rs

1// Author: D.S. Ljungmark <spider@skuggor.se>, Modio AB
2// SPDX-License-Identifier: AGPL-3.0-or-later
3use std::path::Path;
4use tracing::{debug, warn};
5
6#[derive(Clone)]
7pub struct Timefail {
8    timefail_path: Box<Path>,
9    timeadjust_path: Box<Path>,
10}
11
12const TIMEFAIL: &str = "/run/state/timefail";
13const TIMEADJUST: &str = "/run/state/time_adjust";
14
15const DEVEL_TIMEFAIL: &str = "development/timefail";
16const DEVEL_TIMEADJUST: &str = "development/time_adjust";
17use std::default::Default;
18
19impl Timefail {
20    #[must_use]
21    pub fn new(timefail_path: Box<Path>, timeadjust_path: Box<Path>) -> Self {
22        debug!("timefail path: {:?}", &timefail_path);
23        debug!("timeadjust path: {:?}", &timeadjust_path);
24
25        let tf = Self {
26            timefail_path,
27            timeadjust_path,
28        };
29
30        if tf.is_timefail() {
31            warn!(
32                "Timefail flag {:?} exists, clock is assumed wrong.",
33                &tf.timefail_path
34            );
35        };
36        tf
37    }
38}
39impl Default for Timefail {
40    // Create a default timefail object
41    fn default() -> Self {
42        let timefail_path = Path::new(TIMEFAIL);
43        let timeadjust_path = Path::new(TIMEADJUST);
44        Self::new(timefail_path.into(), timeadjust_path.into())
45    }
46}
47impl Timefail {
48    // Create a development path timefail object
49    #[must_use]
50    pub fn development() -> Self {
51        let timefail_path = Path::new(DEVEL_TIMEFAIL);
52        let timeadjust_path = Path::new(DEVEL_TIMEADJUST);
53        Self::new(timefail_path.into(), timeadjust_path.into())
54    }
55    // Create a development path timefail object
56    #[must_use]
57    pub fn session(state: bool) -> Self {
58        if state {
59            Self::development()
60        } else {
61            Self::default()
62        }
63    }
64
65    #[must_use]
66    pub fn is_timefail(&self) -> bool {
67        self.timefail_path.is_file()
68    }
69
70    #[must_use]
71    pub fn is_adjust(&self) -> bool {
72        self.timeadjust_path.is_file()
73    }
74
75    pub async fn get_adjust(&self) -> Option<f32> {
76        use async_std::fs;
77        use std::str::FromStr;
78        let path = self.timeadjust_path.to_path_buf();
79        fs::read_to_string(path)
80            .await
81            .ok()
82            .and_then(|adjstr| f32::from_str(&adjstr).ok())
83    }
84
85    pub async fn remove_adjust(&self) -> Result<(), std::io::Error> {
86        use async_std::fs;
87        let path = self.timeadjust_path.to_path_buf();
88        fs::remove_file(path).await
89    }
90}
91
92#[cfg(test)]
93pub mod tests {
94    use super::*;
95    use crate::testing::Tempbase;
96    use async_std::fs::File;
97    use async_std::prelude::*;
98    use std::error::Error;
99    use test_log::test;
100    use timeout_macro::timeouttest;
101
102    #[test(timeouttest)]
103    async fn test_parse_adjust_nofile() -> Result<(), Box<dyn Error>> {
104        let tbase = Tempbase::new();
105        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
106        assert_eq!(timefail.get_adjust().await, None);
107        Ok(())
108    }
109
110    #[test(timeouttest)]
111    async fn test_parse_adjust_empty() -> Result<(), Box<dyn Error>> {
112        let tbase = Tempbase::new();
113        let adjust_file = tbase.timeadjust_path().to_path_buf();
114        let file = File::create(adjust_file).await?;
115        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
116        file.sync_all().await?;
117        assert_eq!(timefail.get_adjust().await, None);
118        Ok(())
119    }
120
121    #[test(timeouttest)]
122    async fn test_parse_adjust_strings() -> Result<(), Box<dyn Error>> {
123        let tbase = Tempbase::new();
124        let adjust_file = tbase.timeadjust_path().to_path_buf();
125        let mut file = File::create(adjust_file).await?;
126        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
127
128        file.write_all(b"abc123").await?;
129        file.sync_all().await?;
130        assert_eq!(timefail.get_adjust().await, None);
131        Ok(())
132    }
133
134    #[test(timeouttest)]
135    async fn test_parse_adjust_happy() -> Result<(), Box<dyn Error>> {
136        let tbase = Tempbase::new();
137        let mut file = File::create(tbase.timeadjust_path().to_path_buf()).await?;
138        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
139
140        file.write_all(b"1").await?;
141        file.sync_all().await?;
142        assert_eq!(timefail.get_adjust().await, Some(1.0));
143        Ok(())
144    }
145}