use log::{debug, warn};
use std::path::Path;
#[derive(Clone)]
pub struct Timefail {
timefail_path: Box<Path>,
timeadjust_path: Box<Path>,
}
const TIMEFAIL: &str = "/run/state/timefail";
const TIMEADJUST: &str = "/run/state/time_adjust";
const DEVEL_TIMEFAIL: &str = "development/timefail";
const DEVEL_TIMEADJUST: &str = "development/time_adjust";
use std::default::Default;
impl Timefail {
#[must_use]
pub fn new(timefail_path: Box<Path>, timeadjust_path: Box<Path>) -> Self {
debug!("timefail path: {:?}", &timefail_path);
debug!("timeadjust path: {:?}", &timeadjust_path);
let tf = Self {
timefail_path,
timeadjust_path,
};
if tf.is_timefail() {
warn!(
"Timefail flag {:?} exists, clock is assumed wrong.",
&tf.timefail_path
);
};
tf
}
}
impl Default for Timefail {
#[must_use]
fn default() -> Self {
let timefail_path = Path::new(TIMEFAIL);
let timeadjust_path = Path::new(TIMEADJUST);
Self::new(timefail_path.into(), timeadjust_path.into())
}
}
impl Timefail {
#[must_use]
pub fn development() -> Self {
let timefail_path = Path::new(DEVEL_TIMEFAIL);
let timeadjust_path = Path::new(DEVEL_TIMEADJUST);
Self::new(timefail_path.into(), timeadjust_path.into())
}
#[must_use]
pub fn session(state: bool) -> Self {
if state {
Self::development()
} else {
Self::default()
}
}
#[must_use]
pub fn is_timefail(&self) -> bool {
self.timefail_path.is_file()
}
#[must_use]
pub fn is_adjust(&self) -> bool {
self.timeadjust_path.is_file()
}
pub async fn get_adjust(&self) -> Option<f32> {
use std::str::FromStr;
use tokio::fs;
let path = self.timeadjust_path.to_path_buf();
fs::read_to_string(path)
.await
.ok()
.and_then(|adjstr| f32::from_str(&adjstr).ok())
}
pub async fn remove_adjust(&self) -> Result<(), std::io::Error> {
use tokio::fs;
let path = self.timeadjust_path.to_path_buf();
fs::remove_file(path).await
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::testing::Tempbase;
use std::error::Error;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
#[tokio::test]
async fn test_parse_adjust_nofile() -> Result<(), Box<dyn Error>> {
let tbase = Tempbase::new();
let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
assert_eq!(timefail.get_adjust().await, None);
Ok(())
}
#[tokio::test]
async fn test_parse_adjust_empty() -> Result<(), Box<dyn Error>> {
let tbase = Tempbase::new();
let adjust_file = tbase.timeadjust_path().to_path_buf();
let file = File::create(adjust_file).await?;
let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
file.sync_all().await?;
assert_eq!(timefail.get_adjust().await, None);
Ok(())
}
#[tokio::test]
async fn test_parse_adjust_strings() -> Result<(), Box<dyn Error>> {
let tbase = Tempbase::new();
let adjust_file = tbase.timeadjust_path().to_path_buf();
let mut file = File::create(adjust_file).await?;
let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
file.write_all(b"abc123").await?;
file.sync_all().await?;
assert_eq!(timefail.get_adjust().await, None);
Ok(())
}
#[tokio::test]
async fn test_parse_adjust_happy() -> Result<(), Box<dyn Error>> {
let tbase = Tempbase::new();
let mut file = File::create(tbase.timeadjust_path().to_path_buf()).await?;
let timefail = Timefail::new(tbase.timefail_path(), tbase.timeadjust_path());
file.write_all(b"1").await?;
file.sync_all().await?;
assert_eq!(timefail.get_adjust().await, Some(1.0));
Ok(())
}
}