blob: 29518b19e18a4b57db1352248134b5f010ae4c1b [file] [log] [blame]
use crate::streaming::systems::system::System;
use iggy::error::IggyError;
use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::fmt::Display;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use tracing::info;
const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct SystemInfo {
pub version: Version,
pub migrations: Vec<Migration>,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Version {
pub version: String,
pub hash: String,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Migration {
pub id: u32,
pub name: String,
pub hash: String,
pub applied_at: u64,
}
#[derive(Debug)]
pub struct SemanticVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
}
impl System {
pub(crate) async fn load_version(&mut self) -> Result<(), IggyError> {
info!("Loading system info...");
let mut system_info = SystemInfo::default();
if let Err(err) = self.storage.info.load(&mut system_info).await {
match err {
IggyError::ResourceNotFound(_) => {
info!("System info not found, creating...");
self.update_system_info(&mut system_info).await?;
}
_ => return Err(err),
}
}
info!("Loaded {system_info}");
let current_version = SemanticVersion::from_str(VERSION)?;
let loaded_version = SemanticVersion::from_str(&system_info.version.version)?;
if current_version.is_equal_to(&loaded_version) {
info!("System version {current_version} is up to date.");
} else if current_version.is_greater_than(&loaded_version) {
info!("System version {current_version} is greater than {loaded_version}, checking the available migrations...");
self.update_system_info(&mut system_info).await?;
} else {
info!("System version {current_version} is lower than {loaded_version}, possible downgrade.");
self.update_system_info(&mut system_info).await?;
}
Ok(())
}
async fn update_system_info(&self, system_info: &mut SystemInfo) -> Result<(), IggyError> {
system_info.update_version(VERSION);
self.storage.info.save(system_info).await?;
Ok(())
}
}
impl SystemInfo {
pub fn update_version(&mut self, version: &str) {
self.version.version = version.to_string();
let mut hasher = DefaultHasher::new();
self.version.hash.hash(&mut hasher);
self.version.hash = hasher.finish().to_string();
}
}
impl Hash for SystemInfo {
fn hash<H: Hasher>(&self, state: &mut H) {
self.version.version.hash(state);
for migration in &self.migrations {
migration.hash(state);
}
}
}
impl Hash for Migration {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "version: {}", self.version)
}
}
impl Display for SystemInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "system info, {}", self.version)
}
}
impl FromStr for SemanticVersion {
type Err = IggyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut version = s.split('.');
let major = version.next().unwrap().parse::<u32>()?;
let minor = version.next().unwrap().parse::<u32>()?;
let patch = version.next().unwrap().parse::<u32>()?;
Ok(SemanticVersion {
major,
minor,
patch,
})
}
}
impl SemanticVersion {
pub fn is_equal_to(&self, other: &SemanticVersion) -> bool {
self.major == other.major && self.minor == other.minor && self.patch == other.patch
}
pub fn is_greater_than(&self, other: &SemanticVersion) -> bool {
if self.major > other.major {
return true;
}
if self.major < other.major {
return false;
}
if self.minor > other.minor {
return true;
}
if self.minor < other.minor {
return false;
}
if self.patch > other.patch {
return true;
}
if self.patch < other.patch {
return false;
}
false
}
}
impl Display for SemanticVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{major}.{minor}.{patch}",
major = self.major,
minor = self.minor,
patch = self.patch
)
}
}
mod tests {
#[test]
fn should_load_the_expected_version_from_package_definition() {
use super::VERSION;
const CARGO_TOML_VERSION: &str = env!("CARGO_PKG_VERSION");
assert_eq!(VERSION, CARGO_TOML_VERSION);
}
}