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 #[must_use]
41 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 #[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 #[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}