use crate::config::locator;
use chrono::{DateTime, Utc};
use semver::Version;
use serde::Deserialize;
use serde::Serialize;
use serde_json;
use std::fs;
use super::data::project_dir;
const FILE_NAME: &str = "upgrade_check.json";
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct UpgradeCheck {
pub latest_check_time: DateTime<Utc>,
pub max_stable_version: Version,
pub max_version: Version,
}
impl Default for UpgradeCheck {
fn default() -> Self {
Self {
latest_check_time: DateTime::<Utc>::UNIX_EPOCH,
max_stable_version: Version::new(0, 0, 0),
max_version: Version::new(0, 0, 0),
}
}
}
impl UpgradeCheck {
pub fn load() -> Result<Self, locator::Error> {
let path = project_dir()
.map_err(|_| locator::Error::ProjectDirsError())?
.data_dir()
.join(FILE_NAME);
if !path.exists() {
return Ok(Self::default());
}
let data = fs::read(&path)
.map_err(|error| locator::Error::UpgradeCheckReadFailed { path, error })?;
Ok(serde_json::from_slice(data.as_slice())?)
}
pub fn save(&self) -> Result<(), locator::Error> {
let path = project_dir()
.map_err(|_| locator::Error::ProjectDirsError())?
.data_dir()
.join(FILE_NAME);
let path = locator::ensure_directory(path)?;
let data = serde_json::to_string(self).map_err(|_| locator::Error::ConfigSerialization)?;
locator::write_hardened_file(&path, data.as_bytes())
.map_err(|error| locator::Error::UpgradeCheckWriteFailed { path, error })
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
use std::env;
#[test]
#[serial]
fn test_upgrade_check_load_save() {
let temp_dir = tempfile::tempdir().unwrap();
env::remove_var("XDG_DATA_HOME");
env::set_var("STELLAR_DATA_HOME", temp_dir.path());
let default_check = UpgradeCheck::load().unwrap();
assert_eq!(default_check, UpgradeCheck::default());
assert_eq!(
default_check.latest_check_time,
DateTime::<Utc>::from_timestamp_millis(0).unwrap()
);
assert_eq!(default_check.max_stable_version, Version::new(0, 0, 0));
let saved_check = UpgradeCheck {
latest_check_time: DateTime::<Utc>::from_timestamp(1_234_567_890, 0).unwrap(),
max_stable_version: Version::new(1, 2, 3),
max_version: Version::parse("1.2.4-rc.1").unwrap(),
};
saved_check.save().unwrap();
let loaded_check = UpgradeCheck::load().unwrap();
assert_eq!(loaded_check, saved_check);
}
#[cfg(unix)]
#[test]
#[serial]
fn test_upgrade_check_save_uses_0600_permissions() {
use crate::test_utils::with_env_set;
use std::os::unix::fs::PermissionsExt;
let temp_dir = tempfile::tempdir().unwrap();
with_env_set("STELLAR_DATA_HOME", temp_dir.path(), || {
UpgradeCheck::default().save().unwrap();
let path = project_dir().unwrap().data_dir().join(FILE_NAME);
let mode = fs::metadata(&path).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o600, "expected 0600, got {mode:o}");
});
}
}