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    #[must_use]
41    // Create a default timefail object
42    fn default() -> Self {
43        let timefail_path = Path::new(TIMEFAIL);
44        let timeadjust_path = Path::new(TIMEADJUST);
45        Self::new(timefail_path.into(), timeadjust_path.into())
46    }
47}
48impl Timefail {
49    // Create a development path timefail object
50    #[must_use]
51    pub fn development() -> Self {
52        let timefail_path = Path::new(DEVEL_TIMEFAIL);
53        let timeadjust_path = Path::new(DEVEL_TIMEADJUST);
54        Self::new(timefail_path.into(), timeadjust_path.into())
55    }
56    // Create a development path timefail object
57    #[must_use]
58    pub fn session(state: bool) -> Self {
59        if state {
60            Self::development()
61        } else {
62            Self::default()
63        }
64    }
65
66    #[must_use]
67    pub fn is_timefail(&self) -> bool {
68        self.timefail_path.is_file()
69    }
70
71    #[must_use]
72    pub fn is_adjust(&self) -> bool {
73        self.timeadjust_path.is_file()
74    }
75
76    pub async fn get_adjust(&self) -> Option<f32> {
77        use async_std::fs;
78        use std::str::FromStr;
79        let path = self.timeadjust_path.to_path_buf();
80        fs::read_to_string(path)
81            .await
82            .ok()
83            .and_then(|adjstr| f32::from_str(&adjstr).ok())
84    }
85
86    pub async fn remove_adjust(&self) -> Result<(), std::io::Error> {
87        use async_std::fs;
88        let path = self.timeadjust_path.to_path_buf();
89        fs::remove_file(path).await
90    }
91}
92
93#[cfg(test)]
94pub mod tests {
95    use super::*;
96    use crate::testing::Tempbase;
97    use async_std::fs::File;
98    use async_std::prelude::*;
99    use std::error::Error;
100    use test_log::test;
101    use timeout_macro::timeouttest;
102
103    #[test(timeouttest)]
104    async fn test_parse_adjust_nofile() -> Result<(), Box<dyn Error>> {
105        let tbase = Tempbase::new();
106        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
107        assert_eq!(timefail.get_adjust().await, None);
108        Ok(())
109    }
110
111    #[test(timeouttest)]
112    async fn test_parse_adjust_empty() -> Result<(), Box<dyn Error>> {
113        let tbase = Tempbase::new();
114        let adjust_file = tbase.timeadjust_path().to_path_buf();
115        let file = File::create(adjust_file).await?;
116        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
117        file.sync_all().await?;
118        assert_eq!(timefail.get_adjust().await, None);
119        Ok(())
120    }
121
122    #[test(timeouttest)]
123    async fn test_parse_adjust_strings() -> Result<(), Box<dyn Error>> {
124        let tbase = Tempbase::new();
125        let adjust_file = tbase.timeadjust_path().to_path_buf();
126        let mut file = File::create(adjust_file).await?;
127        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
128
129        file.write_all(b"abc123").await?;
130        file.sync_all().await?;
131        assert_eq!(timefail.get_adjust().await, None);
132        Ok(())
133    }
134
135    #[test(timeouttest)]
136    async fn test_parse_adjust_happy() -> Result<(), Box<dyn Error>> {
137        let tbase = Tempbase::new();
138        let mut file = File::create(tbase.timeadjust_path().to_path_buf()).await?;
139        let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
140
141        file.write_all(b"1").await?;
142        file.sync_all().await?;
143        assert_eq!(timefail.get_adjust().await, Some(1.0));
144        Ok(())
145    }
146}