Skip to content

Commit

Permalink
feat: add remote package caching
Browse files Browse the repository at this point in the history
This will improve update times
  • Loading branch information
TheAlexDev23 authored Feb 15, 2024
1 parent 324ae37 commit bcc677a
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 164 deletions.
13 changes: 6 additions & 7 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod tests;

pub trait PackageFinder {
type Error: Display;
fn find_package(&self, package_name: &str) -> Result<Option<RemotePackage>, Self::Error>;
fn find_package(&mut self, package_name: &str) -> Result<Option<RemotePackage>, Self::Error>;
}

pub enum ReinstallOptions {
Expand All @@ -30,10 +30,9 @@ pub enum ReinstallOptions {
Ignore,
}

// TODO: migrate update and force_reinstall to enum
pub fn install_packages<EFind: Display, EDatabase: Display>(
packages: Vec<String>,
package_finder: &impl PackageFinder<Error = EFind>,
package_finder: &mut impl PackageFinder<Error = EFind>,
reinstall_options: &ReinstallOptions,
db: &mut impl PackagesDb<GetError = EDatabase>,
) -> Result<Vec<Action>, InstallError<EDatabase, EFind>> {
Expand Down Expand Up @@ -66,7 +65,7 @@ pub fn remove_packages<EDatabase: Display>(
}

pub fn update_all_packages<EDatabase: Display, EFind: Display>(
package_finder: &impl PackageFinder<Error = EFind>,
package_finder: &mut impl PackageFinder<Error = EFind>,
db: &mut impl PackagesDb<GetError = EDatabase>,
) -> Result<Vec<Action>, UpdateError<EDatabase, EFind>> {
let packages = match db.get_all_packages() {
Expand All @@ -83,7 +82,7 @@ pub fn update_all_packages<EDatabase: Display, EFind: Display>(

pub fn update_packages<EDatabase: Display, EFind: Display>(
package_names: Vec<String>,
package_finder: &impl PackageFinder<Error = EFind>,
package_finder: &mut impl PackageFinder<Error = EFind>,
db: &mut impl PackagesDb<GetError = EDatabase>,
) -> Result<Vec<Action>, UpdateError<EDatabase, EFind>> {
let mut actions: Vec<Action> = Vec::new();
Expand Down Expand Up @@ -135,7 +134,7 @@ pub fn print_package_info<EDatabase: Display>(

fn install_package<EFind: Display, EDatabase: Display>(
package_name: &str,
package_finder: &impl PackageFinder<Error = EFind>,
package_finder: &mut impl PackageFinder<Error = EFind>,
reinstall_options: &ReinstallOptions,
db: &mut impl PackagesDb<GetError = EDatabase>,
) -> Result<LinkedHashSet<Action>, InstallError<EDatabase, EFind>> {
Expand Down Expand Up @@ -184,7 +183,7 @@ fn install_package<EFind: Display, EDatabase: Display>(
}
};

if let std::cmp::Ordering::Greater = remote_version.cmp(&local_version) {
if remote_version.cmp(&local_version) == std::cmp::Ordering::Greater {
actions.insert(Action::Remove(local_package), ());
} else {
info!(
Expand Down
26 changes: 12 additions & 14 deletions src/commands/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,22 @@ mod mock_package_finder;

#[test]
fn test_install_actions_generated_succesfully() {
let (mut mock_db, package_finder) = get_mocks();
let (mut mock_db, mut package_finder) = get_mocks();
let remote_package = package_finder.get_simple_packge();

let install_result = commands::install_packages(
vec![remote_package.package_data.name.clone()],
&package_finder,
&mut package_finder,
&ReinstallOptions::Ignore,
&mut mock_db,
);

assert_actions(install_result, vec![Action::Install(remote_package)]);
}


#[test]
fn test_remove_package_actions_generated_succesfully() {
let (mut mock_db, package_finder) = get_mocks();
let (mut mock_db, mut package_finder) = get_mocks();
let remote_package = package_finder.get_simple_packge();

let local_package = mock_install(&mut mock_db, &remote_package);
Expand All @@ -38,14 +37,14 @@ fn test_remove_package_actions_generated_succesfully() {

#[test]
fn test_installed_package_is_ignored() {
let (mut mock_db, package_finder) = get_mocks();
let (mut mock_db, mut package_finder) = get_mocks();
let remote_package = package_finder.get_simple_packge();

mock_install(&mut mock_db, &remote_package);

let install_result = commands::install_packages(
vec![remote_package.package_data.name.clone()],
&package_finder,
&mut package_finder,
&ReinstallOptions::Ignore,
&mut mock_db,
);
Expand All @@ -67,7 +66,7 @@ fn test_installed_package_is_updated() {

let install_result = commands::install_packages(
vec![package_name],
&package_finder,
&mut package_finder,
&ReinstallOptions::Update,
&mut mock_db,
);
Expand All @@ -83,14 +82,14 @@ fn test_installed_package_is_updated() {

#[test]
fn test_latest_ver_installed_package_is_ignored() {
let (mut mock_db, package_finder) = get_mocks();
let (mut mock_db, mut package_finder) = get_mocks();
let remote_package = package_finder.get_simple_packge();

mock_install(&mut mock_db, &remote_package);

let install_result = commands::install_packages(
vec![remote_package.package_data.name.clone()],
&package_finder,
&mut package_finder,
&ReinstallOptions::Update,
&mut mock_db,
);
Expand All @@ -100,14 +99,14 @@ fn test_latest_ver_installed_package_is_ignored() {

#[test]
fn test_installed_package_is_reinstalled() {
let (mut mock_db, package_finder) = get_mocks();
let (mut mock_db, mut package_finder) = get_mocks();
let remote_package = package_finder.get_simple_packge();

let local_package = mock_install(&mut mock_db, &remote_package);

let install_result = commands::install_packages(
vec![remote_package.package_data.name.clone()],
&package_finder,
&mut package_finder,
&ReinstallOptions::ForceReinstall,
&mut mock_db,
);
Expand All @@ -123,7 +122,7 @@ fn test_installed_package_is_reinstalled() {

#[test]
fn test_remove_package_with_depending_packages_is_not_allowed() {
let (mut mock_db, package_finder) = get_mocks();
let (mut mock_db, mut package_finder) = get_mocks();
let package_with_dependency = package_finder.get_package_with_dependency();
let package_dependency = package_finder
.find_package(&package_with_dependency.dependencies[0])
Expand All @@ -148,7 +147,7 @@ fn test_remove_package_with_depending_packages_is_not_allowed() {

#[test]
fn test_remove_package_removes_depending() {
let (mut mock_db, package_finder) = get_mocks();
let (mut mock_db, mut package_finder) = get_mocks();
let package_with_dependency = package_finder.get_package_with_dependency();
let package_dependency = package_finder
.find_package(&package_with_dependency.dependencies[0])
Expand Down Expand Up @@ -176,7 +175,6 @@ fn test_remove_package_removes_depending() {
);
}


fn assert_actions<Error: std::fmt::Debug>(
result: Result<Vec<Action>, Error>,
expected_actions: Vec<Action>,
Expand Down
6 changes: 3 additions & 3 deletions src/commands/tests/mock_package_finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub struct MockPackageFinder {
impl PackageFinder for MockPackageFinder {
type Error = String;

fn find_package(&self, package_name: &str) -> Result<Option<RemotePackage>, String> {
fn find_package(&mut self, package_name: &str) -> Result<Option<RemotePackage>, String> {
Ok(self.packages_db.get(&String::from(package_name)).cloned())
}
}
Expand Down Expand Up @@ -54,11 +54,11 @@ impl MockPackageFinder {
.version = String::from("0.0.2");
}

pub fn get_simple_packge(&self) -> RemotePackage {
pub fn get_simple_packge(&mut self) -> RemotePackage {
self.find_package("simple_package").unwrap().unwrap()
}

pub fn get_package_with_dependency(&self) -> RemotePackage {
pub fn get_package_with_dependency(&mut self) -> RemotePackage {
self.find_package("package_with_dependency")
.unwrap()
.unwrap()
Expand Down
101 changes: 101 additions & 0 deletions src/default_package_finder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::collections::HashMap;
use std::fs;
use std::io;
use std::io::Read;

use log::{debug, trace, warn};

use reqwest::StatusCode;

use thiserror::Error;

use crate::commands::PackageFinder;
use crate::config::Config;
use crate::package::RemotePackage;

#[derive(Error, Debug)]
pub enum PackageFindError {
#[error("An io error has occured: {0}")]
Read(#[from] io::Error),
#[error("A json error has occured: {0}")]
Json(#[from] serde_json::Error),
}

pub struct DefaultPackageFinder {
from_file: bool,
remotes: Vec<String>,
remote_search_cache: HashMap<String, RemotePackage>,
}
impl DefaultPackageFinder {
pub fn new(from_file: bool, config: &Config) -> DefaultPackageFinder {
DefaultPackageFinder {
from_file,
remotes: config.remotes.values().cloned().collect(),
remote_search_cache: HashMap::new(),
}
}
}
impl PackageFinder for DefaultPackageFinder {
type Error = PackageFindError;
fn find_package(&mut self, package_name: &str) -> Result<Option<RemotePackage>, Self::Error> {
if self.from_file {
let mut path: String = String::from(package_name);
if !path.ends_with(".json") {
path.push_str(".json");
}

if fs::metadata(&path).is_err() {
return Ok(None);
}

let json_content = fs::read_to_string(path)?;
Ok(Some(RemotePackage::from_json(&json_content)?))
} else {
if let Some(remote_package) = self.remote_search_cache.get(&String::from(package_name))
{
trace!("Remote package chache hit for {package_name}");
Ok(Some(remote_package.clone()))
} else {
let mut remotes = self.remotes.iter();
let json_content = loop {
let mut remote = match remotes.next() {
Some(remote) => remote.clone(),
None => return Ok(None),
};

if remote.ends_with('/') {
remote.push_str(format!("/packages/{package_name}/package.json").as_str());
} else {
remote.push_str(format!("packages/{package_name}/package.json").as_str());
}

let mut res = match reqwest::blocking::get(&remote) {
Ok(res) => {
if res.status() != StatusCode::OK {
debug!("Package {package_name} not found in remote {remote}");
continue;
}

res
}
Err(error) => {
warn!("Error while attempting to download package:\n{error}");
continue;
}
};

let mut body = String::new();
res.read_to_string(&mut body)?;
break body;
};

let remote_package = RemotePackage::from_json(&json_content)?;

self.remote_search_cache
.insert(String::from(package_name), remote_package.clone());

Ok(Some(remote_package))
}
}
}
}
45 changes: 14 additions & 31 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::process::exit;

use default_package_finder::DefaultPackageFinder;
use log::{error, trace};
use logger::StdLogger;

Expand All @@ -12,9 +13,9 @@ mod action;
mod commands;
mod config;
mod db;
mod default_package_finder;
mod logger;
mod package;
mod package_finders;

#[cfg(test)]
mod test_helpers;
Expand Down Expand Up @@ -82,44 +83,26 @@ fn main() {
commands::ReinstallOptions::Ignore
};

// Depending on the error of the package_finder install_packages returns different Result types
// This makes it hard to call both of these functions in a non boilerplate way. This seems like
// the one that less code uses. Altough it does get rid of the error itself and just gets it's string
// version to print out later. This can be an issue for future implementations.
if from_file {
commands::install_packages(
packages,
&package_finders::FromFilePackageFinder,
&reinstall_options,
&mut db,
)
.map_err(|e| e.to_string())
} else {
commands::install_packages(
packages,
&package_finders::RemotePackageFinder::new(&config),
&reinstall_options,
&mut db,
)
.map_err(|e| e.to_string())
}
let mut package_finder = DefaultPackageFinder::new(from_file, &config);

commands::install_packages(
packages,
&mut package_finder,
&reinstall_options,
&mut db,
)
.map_err(|e| e.to_string())
}
CommandType::Remove {
packages,
recursive,
} => commands::remove_packages(packages, recursive, &mut db).map_err(|e| e.to_string()),
CommandType::Update { system, packages } => {
let mut package_finder = DefaultPackageFinder::new(false, &config);
if system {
commands::update_all_packages(
&package_finders::RemotePackageFinder::new(&config),
&mut db,
)
commands::update_all_packages(&mut package_finder, &mut db)
} else {
commands::update_packages(
packages,
&package_finders::RemotePackageFinder::new(&config),
&mut db,
)
commands::update_packages(packages, &mut package_finder, &mut db)
}
}
.map_err(|e| e.to_string()),
Expand Down
5 changes: 0 additions & 5 deletions src/package_finders.rs

This file was deleted.

Loading

0 comments on commit bcc677a

Please sign in to comment.