Skip to content

Commit

Permalink
feat(homebrew operation): ✨ cache upgraded formula and cask for speed (
Browse files Browse the repository at this point in the history
…#257)

A cache has been added for the asdf operations when running `omni up`,
this makes it run faster when we already checked if asdf is up to date,
and if the asdf tool is up to date.

This does the same for the homebrew operation.

Closes #229
  • Loading branch information
xaf authored Dec 5, 2023
1 parent a527770 commit 04a4e4e
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 24 deletions.
21 changes: 18 additions & 3 deletions src/internal/cache/asdf_operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ pub struct AsdfOperationCache {
skip_serializing_if = "AsdfOperationUpdateCache::is_empty"
)]
pub update_cache: AsdfOperationUpdateCache,
#[serde(default = "utils::origin_of_time", with = "time::serde::rfc3339")]
#[serde(
default = "utils::origin_of_time",
with = "time::serde::rfc3339",
skip_serializing_if = "utils::is_origin_of_time"
)]
pub updated_at: OffsetDateTime,
}

Expand All @@ -52,15 +56,18 @@ impl AsdfOperationCache {
}

pub fn should_update_asdf(&self) -> bool {
// TODO: add configuration option for the duration?
self.update_cache.should_update_asdf(Duration::days(1))
}

pub fn should_update_asdf_plugin(&self, plugin: &str) -> bool {
// TODO: add configuration option for the duration?
self.update_cache
.should_update_asdf_plugin(plugin, Duration::days(1))
}

pub fn get_asdf_plugin_versions(&self, plugin: &str) -> Option<Vec<String>> {
// TODO: add configuration option for the duration?
self.update_cache
.get_asdf_plugin_versions(plugin, Duration::hours(1))
}
Expand Down Expand Up @@ -135,7 +142,11 @@ pub struct AsdfInstalled {

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AsdfOperationUpdateCache {
#[serde(default = "utils::origin_of_time", with = "time::serde::rfc3339")]
#[serde(
default = "utils::origin_of_time",
with = "time::serde::rfc3339",
skip_serializing_if = "utils::is_origin_of_time"
)]
pub asdf_updated_at: OffsetDateTime,
#[serde(
default = "HashMap::new",
Expand Down Expand Up @@ -210,7 +221,11 @@ impl Empty for AsdfOperationUpdateCache {

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AsdfOperationUpdateCachePluginVersions {
#[serde(default = "utils::origin_of_time", with = "time::serde::rfc3339")]
#[serde(
default = "utils::origin_of_time",
with = "time::serde::rfc3339",
skip_serializing_if = "utils::is_origin_of_time"
)]
pub updated_at: OffsetDateTime,
#[serde(default = "Vec::new", skip_serializing_if = "Vec::is_empty")]
pub versions: Vec<String>,
Expand Down
205 changes: 201 additions & 4 deletions src/internal/cache/homebrew_operation.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::io;

use serde::Deserialize;
use serde::Serialize;
use time::Duration;
use time::OffsetDateTime;

use crate::internal::cache::handler::exclusive;
use crate::internal::cache::handler::shared;
use crate::internal::cache::loaders::get_homebrew_operation_cache;
use crate::internal::cache::loaders::set_homebrew_operation_cache;
use crate::internal::cache::offsetdatetime_hashmap;
use crate::internal::cache::utils;
use crate::internal::cache::utils::Empty;
use crate::internal::cache::CacheObject;
Expand All @@ -21,11 +24,24 @@ pub struct HomebrewOperationCache {
pub installed: Vec<HomebrewInstalled>,
#[serde(default = "Vec::new", skip_serializing_if = "Vec::is_empty")]
pub tapped: Vec<HomebrewTapped>,
#[serde(default = "utils::origin_of_time", with = "time::serde::rfc3339")]
#[serde(
default = "HomebrewOperationUpdateCache::new",
skip_serializing_if = "HomebrewOperationUpdateCache::is_empty"
)]
pub update_cache: HomebrewOperationUpdateCache,
#[serde(
default = "utils::origin_of_time",
with = "time::serde::rfc3339",
skip_serializing_if = "utils::is_origin_of_time"
)]
pub updated_at: OffsetDateTime,
}

impl HomebrewOperationCache {
pub fn updated(&mut self) {
self.updated_at = OffsetDateTime::now_utc();
}

pub fn add_tap(&mut self, workdir_id: &str, tap_name: &str, tapped: bool) -> bool {
let inserted = if let Some(tap) = self.tapped.iter_mut().find(|t| t.name == tap_name) {
tap.tapped = tap.tapped || tapped;
Expand All @@ -41,7 +57,7 @@ impl HomebrewOperationCache {
};

if inserted {
self.updated_at = OffsetDateTime::now_utc();
self.updated();
}

inserted
Expand Down Expand Up @@ -74,16 +90,78 @@ impl HomebrewOperationCache {
};

if inserted {
self.updated_at = OffsetDateTime::now_utc();
self.updated();
}

inserted
}

pub fn updated_homebrew(&mut self) {
self.update_cache.updated_homebrew();
self.updated();
}

pub fn should_update_homebrew(&self) -> bool {
// TODO: add configuration option for the duration?
self.update_cache.should_update_homebrew(Duration::days(1))
}

pub fn updated_install(
&mut self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
) {
self.update_cache
.updated_homebrew_install(install_name, install_version, is_cask);
self.updated();
}

pub fn should_update_install(
&self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
) -> bool {
self.update_cache.should_update_homebrew_install(
install_name,
install_version,
is_cask,
// TODO: add configuration option for the duration?
Duration::days(1),
)
}

pub fn checked_install(
&mut self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
) {
self.update_cache
.checked_homebrew_install(install_name, install_version, is_cask);
self.updated();
}

pub fn should_check_install(
&self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
) -> bool {
self.update_cache.should_check_homebrew_install(
install_name,
install_version,
is_cask,
// TODO: add configuration option for the duration?
Duration::minutes(5),
)
}
}

impl Empty for HomebrewOperationCache {
fn is_empty(&self) -> bool {
self.installed.is_empty() && self.tapped.is_empty()
self.installed.is_empty() && self.tapped.is_empty() && self.update_cache.is_empty()
}
}

Expand All @@ -92,6 +170,7 @@ impl CacheObject for HomebrewOperationCache {
Self {
installed: Vec::new(),
tapped: Vec::new(),
update_cache: HomebrewOperationUpdateCache::new(),
updated_at: utils::origin_of_time(),
}
}
Expand Down Expand Up @@ -137,3 +216,121 @@ pub struct HomebrewTapped {
#[serde(default = "BTreeSet::new", skip_serializing_if = "BTreeSet::is_empty")]
pub required_by: BTreeSet<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HomebrewOperationUpdateCache {
#[serde(
default = "utils::origin_of_time",
with = "time::serde::rfc3339",
skip_serializing_if = "utils::is_origin_of_time"
)]
pub homebrew_updated_at: OffsetDateTime,
#[serde(
default = "HashMap::new",
skip_serializing_if = "HashMap::is_empty",
with = "offsetdatetime_hashmap"
)]
pub install_updated_at: HashMap<String, OffsetDateTime>,
#[serde(
default = "HashMap::new",
skip_serializing_if = "HashMap::is_empty",
with = "offsetdatetime_hashmap"
)]
pub install_checked_at: HashMap<String, OffsetDateTime>,
}

impl HomebrewOperationUpdateCache {
pub fn new() -> Self {
Self {
homebrew_updated_at: utils::origin_of_time(),
install_updated_at: HashMap::new(),
install_checked_at: HashMap::new(),
}
}

fn install_key(
&self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
) -> String {
format!(
"{}{}{}",
if is_cask { "cask:" } else { "formula:" },
install_name,
if let Some(install_version) = install_version {
format!("@{}", install_version)
} else {
"".to_string()
},
)
}

pub fn updated_homebrew(&mut self) {
self.homebrew_updated_at = OffsetDateTime::now_utc();
}

pub fn should_update_homebrew(&self, expire_after: Duration) -> bool {
(self.homebrew_updated_at + expire_after) < OffsetDateTime::now_utc()
}

pub fn updated_homebrew_install(
&mut self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
) {
let key = self.install_key(install_name, install_version, is_cask);
self.install_updated_at
.insert(key, OffsetDateTime::now_utc());
}

pub fn should_update_homebrew_install(
&self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
expire_after: Duration,
) -> bool {
let key = self.install_key(install_name, install_version, is_cask);
if let Some(install_updated_at) = self.install_updated_at.get(&key) {
(*install_updated_at + expire_after) < OffsetDateTime::now_utc()
} else {
true
}
}

pub fn checked_homebrew_install(
&mut self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
) {
let key = self.install_key(install_name, install_version, is_cask);
self.install_checked_at
.insert(key, OffsetDateTime::now_utc());
}

pub fn should_check_homebrew_install(
&self,
install_name: &str,
install_version: Option<String>,
is_cask: bool,
expire_after: Duration,
) -> bool {
let key = self.install_key(install_name, install_version, is_cask);
if let Some(install_checked_at) = self.install_checked_at.get(&key) {
(*install_checked_at + expire_after) < OffsetDateTime::now_utc()
} else {
true
}
}
}

impl Empty for HomebrewOperationUpdateCache {
fn is_empty(&self) -> bool {
self.install_updated_at.is_empty()
&& self.install_checked_at.is_empty()
&& self.homebrew_updated_at == utils::origin_of_time()
}
}
4 changes: 4 additions & 0 deletions src/internal/cache/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub fn origin_of_time() -> OffsetDateTime {
OffsetDateTime::UNIX_EPOCH
}

pub fn is_origin_of_time(value: &OffsetDateTime) -> bool {
*value == origin_of_time()
}

// pub fn entry_expired_option<T: Expires>(entry: &Option<T>) -> bool {
// if let Some(entry) = entry {
// entry.expired()
Expand Down
Loading

0 comments on commit 04a4e4e

Please sign in to comment.