1use 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 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 #[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 #[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}