Skip to content

Commit

Permalink
refactor(package): use binary checksum in install path
Browse files Browse the repository at this point in the history
  • Loading branch information
QaidVoid committed Oct 10, 2024
1 parent 555737c commit 4a6e3c4
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 82 deletions.
2 changes: 2 additions & 0 deletions src/core/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ pub static BIN_PATH: LazyLock<PathBuf> =
LazyLock::new(|| build_path(&CONFIG.soar_path).unwrap().join("bin"));
pub static INSTALL_TRACK_PATH: LazyLock<PathBuf> =
LazyLock::new(|| build_path(&CONFIG.soar_path).unwrap().join("installs"));
pub static PACKAGES_PATH: LazyLock<PathBuf> =
LazyLock::new(|| build_path(&CONFIG.soar_path).unwrap().join("packages"));
17 changes: 14 additions & 3 deletions src/core/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
};

use super::constant::{BIN_PATH, INSTALL_TRACK_PATH};
use super::constant::{BIN_PATH, INSTALL_TRACK_PATH, PACKAGES_PATH};

/// Expands the environment variables and user home directory in a given path.
pub fn build_path(path: &str) -> Result<PathBuf> {
Expand Down Expand Up @@ -93,7 +93,7 @@ pub fn parse_size(size_str: &str) -> Option<u64> {
None
}

pub async fn validate_checksum(checksum: &str, file_path: &Path) -> Result<()> {
pub async fn calculate_checksum(file_path: &Path) -> Result<String> {
let mut file = File::open(&file_path).await?;

let mut hasher = blake3::Hasher::new();
Expand All @@ -108,7 +108,11 @@ pub async fn validate_checksum(checksum: &str, file_path: &Path) -> Result<()> {

file.flush().await?;

let final_checksum = hasher.finalize().to_hex().to_string();
Ok(hasher.finalize().to_hex().to_string())
}

pub async fn validate_checksum(checksum: &str, file_path: &Path) -> Result<()> {
let final_checksum = calculate_checksum(file_path).await?;
if final_checksum == *checksum {
Ok(())
} else {
Expand All @@ -133,6 +137,13 @@ pub async fn setup_required_paths() -> Result<()> {
))?;
}

if !PACKAGES_PATH.exists() {
fs::create_dir_all(&*PACKAGES_PATH).await.context(format!(
"Failed to create path: {}",
PACKAGES_PATH.to_string_lossy()
))?;
}

Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub async fn init() -> Result<()> {
registry.query(&query).await?;
}
Commands::ListPackages { root_path } => {
registry.list(root_path.as_deref())?;
registry.list(root_path.as_deref()).await?;
}
Commands::Inspect { package } => {
registry.inspect(&package).await?;
Expand Down
45 changes: 29 additions & 16 deletions src/registry/installed.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::path::Path;

use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use tokio::fs;

use crate::{
core::{
config::CONFIG,
constant::INSTALL_TRACK_PATH,
util::{build_path, format_bytes, parse_size},
util::{format_bytes, parse_size},
},
registry::package::parse_package_query,
};
Expand Down Expand Up @@ -65,27 +66,31 @@ impl InstalledPackages {
self.packages.iter().any(|installed| {
installed.repo_name == package.repo_name
&& installed.root_path == package.root_path
&& installed.name == package.package.full_name()
&& installed.name == package.package.full_name('-')
})
}

fn find_package_mut(&mut self, package: &ResolvedPackage) -> Option<&mut InstalledPackage> {
self.packages.iter_mut().find(|installed| {
installed.repo_name == package.repo_name
&& installed.root_path == package.root_path
&& installed.name == package.package.full_name()
&& installed.name == package.package.full_name('-')
})
}

pub fn find_package(&self, package: &ResolvedPackage) -> Option<&InstalledPackage> {
self.packages.iter().find(|installed| {
installed.repo_name == package.repo_name
&& installed.root_path == package.root_path
&& installed.name == package.package.full_name()
&& installed.name == package.package.full_name('-')
})
}

pub async fn register_package(&mut self, resolved_package: &ResolvedPackage) -> Result<()> {
pub async fn register_package(
&mut self,
resolved_package: &ResolvedPackage,
checksum: &str,
) -> Result<()> {
let package = resolved_package.package.to_owned();
if let Some(installed) = self.find_package_mut(resolved_package) {
installed.version = package.version.clone();
Expand All @@ -94,10 +99,10 @@ impl InstalledPackages {
let new_installed = InstalledPackage {
repo_name: resolved_package.repo_name.to_owned(),
root_path: resolved_package.root_path.to_owned(),
name: package.full_name(),
name: package.full_name('-'),
bin_name: package.bin_name,
version: package.version,
checksum: package.bsum,
checksum: checksum.to_owned(),
size: parse_size(&package.size).unwrap_or_default(),
timestamp: Utc::now(),
};
Expand All @@ -115,9 +120,8 @@ impl InstalledPackages {
self.packages.retain(|installed| {
!(installed.repo_name == resolved_package.repo_name
&& installed.root_path == resolved_package.root_path
&& installed.name == resolved_package.package.full_name())
&& installed.name == resolved_package.package.full_name('-'))
});
println!("NOW: {:#?}", self.packages);
}
false => {
return Err(anyhow::anyhow!(
Expand Down Expand Up @@ -171,17 +175,13 @@ impl InstalledPackages {

resolved_packages.iter().for_each(|package| {
println!(
"- [{}] {}:{}-{} ({}/{}-{}/bin) ({})",
"- [{}] {}:{}-{} ({}-{}/bin) ({})",
package.root_path,
package.name,
package.name,
package.version,
build_path(&CONFIG.soar_path)
.unwrap()
.join("packages")
.to_string_lossy(),
package.checksum,
package.name,
package.version,
format_bytes(package.size)
);

Expand Down Expand Up @@ -216,4 +216,17 @@ impl InstalledPackages {

Ok(())
}

pub fn reverse_package_search(&self, path: &Path) -> Option<InstalledPackage> {
let path_str = path.to_string_lossy();
if path_str.len() > 64 {
let checksum = &path_str[..64];
self.packages
.iter()
.find(|package| package.checksum == checksum)
.cloned()
} else {
None
}
}
}
28 changes: 21 additions & 7 deletions src/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl PackageRegistry {
println!(
"[{}] {}: {} {}",
pkg.root_path,
pkg.package.full_name(),
pkg.package.full_name('/'),
pkg.package.description,
installed
);
Expand Down Expand Up @@ -155,7 +155,9 @@ impl PackageRegistry {
if let Some(installed) = installed_pkg {
print_data(
"Install Path",
&pkg.package.get_install_path()?.to_string_lossy(),
&pkg.package
.get_install_path(&installed.checksum)
.to_string_lossy(),
);
print_data(
"Install Date",
Expand All @@ -179,7 +181,7 @@ impl PackageRegistry {
installed_guard.info(package_names, &self.storage)
}

pub fn list(&self, root_path: Option<&str>) -> Result<()> {
pub async fn list(&self, root_path: Option<&str>) -> Result<()> {
let root_path = match root_path {
Some(rp) => match rp.to_lowercase().as_str() {
"base" => Ok(Some(RootPath::Base)),
Expand All @@ -191,17 +193,29 @@ impl PackageRegistry {
}?;

let packages = self.storage.list_packages(root_path);
packages.iter().for_each(|resolved_package| {
for resolved_package in packages {
let package = resolved_package.package.clone();
let variant_prefix = package
.variant
.map(|variant| format!("{}-", variant))
.unwrap_or_default();
let installed_guard = self.installed_packages.lock().await;
let install_prefix = if installed_guard.is_installed(&resolved_package) {
"+"
} else {
"-"
};
println!(
"- [{}] {}:{}-{} ({})",
"[{}] [{}] {}{}:{}-{} ({})",
install_prefix,
resolved_package.root_path,
variant_prefix,
package.name,
package.name,
package.version,
package.size
);
});
}
Ok(())
}

Expand All @@ -224,7 +238,7 @@ pub fn select_package_variant(packages: &[ResolvedPackage]) -> Result<&ResolvedP
" [{}] [{}] {}: {}",
i + 1,
package.root_path,
package.package.full_name(),
package.package.full_name('/'),
package.package.description
);
}
Expand Down
65 changes: 53 additions & 12 deletions src/registry/package/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use tokio::{fs, io::AsyncWriteExt, sync::Mutex};

use crate::{
core::{
constant::BIN_PATH,
util::{format_bytes, validate_checksum},
constant::{BIN_PATH, PACKAGES_PATH},
util::{calculate_checksum, format_bytes, validate_checksum},
},
registry::installed::InstalledPackages,
};
Expand All @@ -22,7 +22,11 @@ pub struct Installer {

impl Installer {
pub fn new(package: &ResolvedPackage, install_path: PathBuf) -> Self {
let temp_path = install_path.with_extension("part");
let temp_path = PACKAGES_PATH
.join("tmp")
.join(package.package.full_name('-'))
.with_extension("part");
println!("TMP PATH: {:#?}", temp_path);
Self {
resolved_package: package.to_owned(),
install_path,
Expand All @@ -44,7 +48,7 @@ impl Installer {
.await
.is_installed(&self.resolved_package);

let prefix = format!("[{}/{}] {}", idx + 1, total, package.full_name());
let prefix = format!("[{}/{}] {}", idx + 1, total, package.full_name('/'));

if !force && is_installed {
println!("{}: Package is already installed", prefix);
Expand All @@ -55,11 +59,11 @@ impl Installer {
println!("{}: Reinstalling package", prefix);
}

if let Some(parent) = self.install_path.parent() {
if let Some(parent) = self.temp_path.parent() {
fs::create_dir_all(parent).await.context(format!(
"{}: Failed to create install directory {}",
"{}: Failed to create temp directory {}",
prefix,
self.install_path.to_string_lossy()
self.temp_path.to_string_lossy()
))?;
}

Expand Down Expand Up @@ -116,7 +120,7 @@ impl Installer {
if package.bsum == "null" {
eprintln!(
"Missing checksum for {}. Installing anyway.",
package.full_name()
package.full_name('/')
);
} else {
let result = validate_checksum(&package.bsum, &self.temp_path).await;
Expand All @@ -136,13 +140,24 @@ impl Installer {
}
}
}
let checksum = calculate_checksum(temp_path).await?;

self.install_path = package.get_install_path(&checksum);
if let Some(parent) = self.install_path.parent() {
fs::create_dir_all(parent).await.context(format!(
"{}: Failed to create install directory {}",
prefix,
self.install_path.to_string_lossy()
))?;
}

self.save_file().await?;
self.symlink_bin().await?;
self.symlink_bin(&installed_packages).await?;

{
let mut installed_packages = installed_packages.lock().await;
installed_packages
.register_package(&self.resolved_package)
.register_package(&self.resolved_package, &checksum)
.await?;
}

Expand All @@ -164,10 +179,36 @@ impl Installer {
Ok(())
}

async fn symlink_bin(&self) -> Result<()> {
async fn symlink_bin(&self, installed_packages: &Arc<Mutex<InstalledPackages>>) -> Result<()> {
let package = &self.resolved_package.package;
let install_path = &self.install_path;
let symlink_path = &BIN_PATH.join(&self.resolved_package.package.bin_name);
let symlink_path = &BIN_PATH.join(&package.bin_name);
let installed_guard = installed_packages.lock().await;
if symlink_path.exists() {
if let Ok(link) = symlink_path.read_link() {
if &link != install_path {
if let Some(path_owner) =
installed_guard.reverse_package_search(link.strip_prefix(&*PACKAGES_PATH)?)
{
println!(
"Warning: The package {} owns the binary {}",
path_owner.name, &package.bin_name
);
print!(
"Do you want to switch to {} (y/N)? ",
package.full_name('/')
);
std::io::stdout().flush()?;

let mut response = String::new();
std::io::stdin().read_line(&mut response)?;

if !response.trim().eq_ignore_ascii_case("y") {
return Ok(());
}
}
}
}
fs::remove_file(symlink_path).await?;
}
fs::symlink(&install_path, &symlink_path)
Expand Down
Loading

0 comments on commit 4a6e3c4

Please sign in to comment.