Skip to content

Commit

Permalink
Merge pull request #10 from thunderstore-io/package-refactor
Browse files Browse the repository at this point in the history
Refactor package struct, implement metadata retrieval for r2modman
  • Loading branch information
ethangreen-dev authored Apr 23, 2024
2 parents 19b0ebb + 734f29a commit 189a470
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 67 deletions.
18 changes: 8 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::error::Error;
use crate::game::{ecosystem, registry};
use crate::game::import::{self, ImportBase, ImportOverrides};
use crate::package::resolver::DependencyGraph;
use crate::package::Package;
use crate::project::lock::LockFile;
use crate::project::overrides::ProjectOverrides;
use crate::project::Project;
Expand Down Expand Up @@ -334,17 +335,14 @@ async fn main() -> Result<(), Error> {
let lock = LockFile::open_or_new(&project.lockfile_path)?;
let graph = DependencyGraph::from_graph(lock.package_graph);

println!("Installed packages:");



for package in graph.digest() {
println!(
"- {}-{} ({})",
package.namespace.bold(),
package.name.bold(),
package.version.to_string().truecolor(90, 90, 90)
);
let package = Package::from_any(package).await?;
let Some(meta) = package.get_metadata().await? else {
continue
};

let str = serde_json::to_string_pretty(&meta)?;
println!("{str}");
}

Ok(())
Expand Down
5 changes: 4 additions & 1 deletion src/package/install/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ impl Installer {
pub async fn load_and_prepare(package: &Package) -> Result<Installer, Error> {
// Temp, we'll figure out a good solution from the progress reporter later.
let test = VoidProgress {};
let cache_dir = package.resolve(test.add_bar().as_ref()).await?;
let cache_dir = match package.get_path().await {
Some(x) => x,
None => package.download(test.add_bar().as_ref()).await?
};

let manifest = {
let path = cache_dir.join("installer.json");
Expand Down
90 changes: 38 additions & 52 deletions src/package/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod install;
pub mod resolver;

use std::borrow::Borrow;
use std::fs::File;
use std::io::{ErrorKind, Read, Seek};
use std::path::{Path, PathBuf};

Expand All @@ -23,6 +24,14 @@ use crate::TCLI_HOME;

use self::index::PackageIndex;

#[derive(Serialize, Deserialize, Debug)]
pub struct PackageMetadata {
#[serde(flatten)]
manifest: PackageManifestV1,
reference: String,
icon: PathBuf,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PackageSource {
Remote(String),
Expand All @@ -43,7 +52,7 @@ pub struct Package {
impl Package {
/// Attempt to resolve the package from the local cache or remote.
/// This does not download the package, it just finds its "source".
pub async fn resolve_new(ident: impl Borrow<PackageReference>) -> Result<Self, Error> {
pub async fn from_any(ident: impl Borrow<PackageReference>) -> Result<Self, Error> {
if cache::get_cache_location(ident.borrow()).exists() {
return Package::from_cache(ident).await;
}
Expand Down Expand Up @@ -110,7 +119,9 @@ impl Package {
/// Load package metadata from an arbitrary path, extracting it into the cache if it passes
// manifest validation.
pub async fn from_path(ident: PackageReference, path: &Path) -> Result<Self, Error> {
let package = Package::from_repo(ident).await?;
// let package = Package::from_repo(ident).await?;
add_to_cache(&ident, File::open(path).map_fs_error(path)?)?;
let package = Package::from_cache(ident).await?;

Ok(Package {
identifier: package.identifier,
Expand All @@ -119,64 +130,39 @@ impl Package {
})
}

/// Resolve the package into a discrete path. This will download the package if it is not cached,
// otherwise it will return its cached path.
pub async fn resolve(&self, reporter: &dyn ProgressBarTrait) -> Result<PathBuf, Error> {
/// Resolve the package into a discrete path, returning None if it does not exist locally.
pub async fn get_path(&self) -> Option<PathBuf> {
match &self.source {
PackageSource::Local(path) => add_to_cache(
&self.identifier,
std::fs::File::open(path).map_fs_error(path)?,
),
PackageSource::Remote(_) => self.download(reporter).await,
PackageSource::Cache(path) => Ok(path.clone()),
File::open(path).map_fs_error(path).ok()?,
).ok(),
PackageSource::Cache(path) => Some(path.clone()),
PackageSource::Remote(_) => None,
}
}

pub async fn add(
&self,
project_state: &Path,
reporter: Box<dyn ProgressBarTrait>,
) -> Result<(), Error> {
let cache_path = self.resolve(reporter.as_ref()).await?;
let install_dir = project_state.join(self.identifier.to_string());

if install_dir.is_dir() {
fs::remove_dir_all(&install_dir)
.await
.map_fs_error(&install_dir)?;
}

for item in walkdir::WalkDir::new(&cache_path).into_iter() {
let item = item?;

let dest_path = install_dir.join(item.path().strip_prefix(&cache_path).unwrap());

if item.file_type().is_dir() {
tokio::fs::create_dir_all(&dest_path)
.await
.map_fs_error(&dest_path)?;
} else if item.file_type().is_file() {
tokio::fs::copy(item.path(), &dest_path)
.await
.map_fs_error(&dest_path)?;
}
}

let finished_msg = format!(
"{} {}-{} ({})",
"[✓]".green(),
self.identifier.namespace.bold(),
self.identifier.name.bold(),
self.identifier.version.to_string().truecolor(90, 90, 90)
);

reporter.println(&finished_msg);
reporter.finish_and_clear();

Ok(())
/// Get the metadata associated with this package. This will return None
/// the package does not exist locally.
pub async fn get_metadata(&self) -> Result<Option<PackageMetadata>, Error> {
let Some(package_dir) = self.get_path().await else {
return Ok(None);
};
let manifest = {
let str = fs::read_to_string(package_dir.join("manifest.json")).await?;
serde_json::from_str::<PackageManifestV1>(&str)?
};
let icon = package_dir.join("icon.png");
let reference = package_dir.file_name().unwrap().to_string_lossy().to_string();

Ok(Some(PackageMetadata {
manifest,
reference,
icon,
}))
}

async fn download(&self, reporter: &dyn ProgressBarTrait) -> Result<PathBuf, Error> {
pub async fn download(&self, reporter: &dyn ProgressBarTrait) -> Result<PathBuf, Error> {
let PackageSource::Remote(package_source) = &self.source else {
panic!("Invalid use, this is a local package.")
};
Expand Down
14 changes: 10 additions & 4 deletions src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ impl Project {
let packages = try_join_all(
packages
.into_iter()
.map(|x| async move { Package::resolve_new(x).await }),
.map(|x| async move { Package::from_any(x).await }),
)
.await?;

Expand All @@ -306,7 +306,10 @@ impl Project {
let bar = bar.as_ref();

// Resolve the package, either downloading it or returning its cached path.
let package_dir = package.resolve(bar).await?;
let package_dir = match package.get_path().await {
Some(x) => x,
None => package.download(bar).await?
};
let tracked_files = installer
.install_package(
&package,
Expand Down Expand Up @@ -376,7 +379,7 @@ impl Project {
let packages = try_join_all(
packages
.into_iter()
.map(|x| async move { Package::resolve_new(x).await }),
.map(|x| async move { Package::from_any(x).await }),
)
.await?;

Expand All @@ -385,7 +388,10 @@ impl Project {
let bar = multi.add_bar();
let bar = bar.as_ref();

let package_dir = package.resolve(bar).await?;
let package_dir = match package.get_path().await {
Some(x) => x,
None => package.download(bar).await?
};
let state_entry = statefile.state.get(&package.identifier);

let tracked_files = state_entry
Expand Down

0 comments on commit 189a470

Please sign in to comment.