diff --git a/src/cli/commands/execute.rs b/src/cli/commands/execute.rs index 3d7599f..0bcaf69 100644 --- a/src/cli/commands/execute.rs +++ b/src/cli/commands/execute.rs @@ -12,8 +12,9 @@ use pesde::{ download_and_link::DownloadAndLinkOptions, linking::generator::generate_bin_linking_module, manifest::target::TargetKind, - names::PackageName, + names::{PackageName, PackageNames}, source::{ + ids::PackageId, pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource}, traits::{ DownloadOptions, GetTargetOptions, PackageSource, RefreshOptions, ResolveOptions, @@ -81,7 +82,7 @@ impl ExecuteCommand { .context("failed to refresh source")?; let version_req = self.package.1.unwrap_or(VersionReq::STAR); - let Some((version, pkg_ref)) = ('finder: { + let Some((id, pkg_ref)) = ('finder: { let specifier = PesdeDependencySpecifier { name: self.package.0.clone(), version: version_req.clone(), @@ -89,7 +90,7 @@ impl ExecuteCommand { target: None, }; - if let Some(res) = source + if let Some((v_id, pkg_ref)) = source .resolve( &specifier, &ResolveOptions { @@ -103,7 +104,10 @@ impl ExecuteCommand { .1 .pop_last() { - break 'finder Some(res); + break 'finder Some(( + PackageId::new(PackageNames::Pesde(self.package.0.clone()), v_id), + pkg_ref, + )); } source @@ -119,6 +123,12 @@ impl ExecuteCommand { .context("failed to resolve package")? .1 .pop_last() + .map(|(v_id, pkg_ref)| { + ( + PackageId::new(PackageNames::Pesde(self.package.0.clone()), v_id), + pkg_ref, + ) + }) }) else { anyhow::bail!( "no Lune or Luau package could be found for {}@{version_req}", @@ -141,6 +151,8 @@ impl ExecuteCommand { project.auth_config().clone(), ); + let id = Arc::new(id); + let fs = source .download( &pkg_ref, @@ -148,6 +160,7 @@ impl ExecuteCommand { project: project.clone(), reqwest: reqwest.clone(), reporter: Arc::new(()), + id: id.clone(), }, ) .await @@ -163,6 +176,7 @@ impl ExecuteCommand { &GetTargetOptions { project: project.clone(), path: Arc::from(tempdir.path()), + id: id.clone(), }, ) .await @@ -176,10 +190,7 @@ impl ExecuteCommand { .context("failed to build dependency graph")?; multi_progress.suspend(|| { - eprintln!( - "{}", - format!("using {}", format!("{}@{version}", pkg_ref.name).bold()).dimmed() - ) + eprintln!("{}", format!("using {}", format!("{id}").bold()).dimmed()) }); root_progress.reset(); diff --git a/src/cli/commands/init.rs b/src/cli/commands/init.rs index 58ae284..602c29b 100644 --- a/src/cli/commands/init.rs +++ b/src/cli/commands/init.rs @@ -6,18 +6,19 @@ use inquire::validator::Validation; use pesde::{ errors::ManifestReadError, manifest::{target::TargetKind, DependencyType}, - names::PackageName, + names::{PackageName, PackageNames}, source::{ git_index::GitBasedSource, + ids::PackageId, pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource}, specifiers::DependencySpecifiers, - traits::{PackageSource, RefreshOptions, ResolveOptions}, + traits::{GetTargetOptions, PackageSource, RefreshOptions, ResolveOptions}, PackageSources, }, Project, RefreshedSources, DEFAULT_INDEX_NAME, SCRIPTS_LINK_FOLDER, }; use semver::VersionReq; -use std::{fmt::Display, str::FromStr}; +use std::{fmt::Display, path::Path, str::FromStr, sync::Arc}; #[derive(Debug, Args)] pub struct InitCommand {} @@ -197,7 +198,7 @@ impl InitCommand { let (v_id, pkg_ref) = source .resolve( &PesdeDependencySpecifier { - name: scripts_pkg_name, + name: scripts_pkg_name.clone(), version: VersionReq::STAR, index: None, target: None, @@ -214,8 +215,22 @@ impl InitCommand { .pop_last() .context("scripts package not found")?; - let Some(scripts) = pkg_ref.target.scripts().filter(|s| !s.is_empty()) else { - anyhow::bail!("scripts package has no scripts. this is an issue with the index") + let id = Arc::new(PackageId::new(PackageNames::Pesde(scripts_pkg_name), v_id)); + + let target = source + .get_target( + &pkg_ref, + &GetTargetOptions { + project: project.clone(), + // HACK: the pesde package source doesn't use the path, so we can just use an empty one + path: Arc::from(Path::new("")), + id: id.clone(), + }, + ) + .await?; + + let Some(scripts) = target.scripts().filter(|s| !s.is_empty()) else { + anyhow::bail!("scripts package has no scripts.") }; let scripts_field = &mut manifest["scripts"] @@ -231,9 +246,9 @@ impl InitCommand { .or_insert(toml_edit::Item::Table(toml_edit::Table::new())); let field = &mut dev_deps["scripts"]; - field["name"] = toml_edit::value(pkg_ref.name.to_string()); - field["version"] = toml_edit::value(format!("^{}", v_id.version())); - field["target"] = toml_edit::value(v_id.target().to_string()); + field["name"] = toml_edit::value(id.name().to_string()); + field["version"] = toml_edit::value(format!("^{}", id.version_id().version())); + field["target"] = toml_edit::value(id.version_id().target().to_string()); for (alias, (spec, ty)) in pkg_ref.dependencies { if ty != DependencyType::Peer { @@ -247,8 +262,11 @@ impl InitCommand { let field = &mut dev_deps[alias]; field["name"] = toml_edit::value(spec.name.to_string()); field["version"] = toml_edit::value(spec.version.to_string()); - field["target"] = - toml_edit::value(spec.target.unwrap_or_else(|| *v_id.target()).to_string()); + field["target"] = toml_edit::value( + spec.target + .unwrap_or_else(|| *id.version_id().target()) + .to_string(), + ); } } else { println!( diff --git a/src/cli/commands/patch.rs b/src/cli/commands/patch.rs index b0fba03..53712a6 100644 --- a/src/cli/commands/patch.rs +++ b/src/cli/commands/patch.rs @@ -57,6 +57,7 @@ impl PatchCommand { project: project.clone(), reqwest, reporter: Arc::new(()), + id: Arc::new(id), }, ) .await? diff --git a/src/cli/commands/publish.rs b/src/cli/commands/publish.rs index 3c4a314..83df811 100644 --- a/src/cli/commands/publish.rs +++ b/src/cli/commands/publish.rs @@ -131,6 +131,7 @@ impl PublishCommand { .join(PACKAGES_CONTAINER_NAME) .join(node.container_folder(id)); + let id = Arc::new(id.clone()); let node = node.clone(); let refreshed_sources = refreshed_sources.clone(); @@ -151,6 +152,7 @@ impl PublishCommand { &GetTargetOptions { project, path: Arc::from(container_folder), + id, }, ) .await?; diff --git a/src/cli/commands/run.rs b/src/cli/commands/run.rs index 23566ff..bcc5a5b 100644 --- a/src/cli/commands/run.rs +++ b/src/cli/commands/run.rs @@ -111,6 +111,7 @@ impl RunCommand { &GetTargetOptions { project, path: Arc::from(container_folder.as_path()), + id: Arc::new(id), }, ) .await?; diff --git a/src/download.rs b/src/download.rs index 1186307..32bac18 100644 --- a/src/download.rs +++ b/src/download.rs @@ -146,7 +146,7 @@ impl Project { let refreshed_sources = refreshed_sources.clone(); let package_dir = project.package_dir().to_path_buf(); let semaphore = semaphore.clone(); - let package_id = package_id.clone(); + let package_id = Arc::new(package_id.clone()); let node = node.clone(); async move { @@ -189,6 +189,7 @@ impl Project { &DownloadOptions { project: project.clone(), reqwest, + id: package_id.clone(), reporter: Arc::new(progress_reporter), }, ) @@ -201,6 +202,7 @@ impl Project { &DownloadOptions { project: project.clone(), reqwest, + id: package_id.clone(), reporter: Arc::new(()), }, ) @@ -225,6 +227,7 @@ impl Project { &GetTargetOptions { project, path: Arc::from(container_folder), + id: package_id.clone(), }, ) .await @@ -236,7 +239,7 @@ impl Project { } let downloaded_node = DownloadedDependencyGraphNode { node, target }; - Ok((downloaded_node, package_id)) + Ok((downloaded_node, Arc::into_inner(package_id).unwrap())) } .instrument(span) }) diff --git a/src/graph.rs b/src/graph.rs index 43dcb59..82cc70e 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -50,7 +50,7 @@ impl DependencyGraphNode { if self.pkg_ref.like_wally() { return PathBuf::from(format!( "{}_{}@{}", - package_id.name().as_str().0, + name.as_str().0, name.as_str().1, version )) diff --git a/src/source/pesde/mod.rs b/src/source/pesde/mod.rs index dbc7c2d..c9a4d46 100644 --- a/src/source/pesde/mod.rs +++ b/src/source/pesde/mod.rs @@ -94,6 +94,25 @@ impl PesdePackageSource { .await .unwrap() } + + fn read_index_file( + &self, + name: &PackageName, + project: &Project, + ) -> Result, errors::ReadIndexFileError> { + let (scope, name) = name.as_str(); + let repo = gix::open(self.path(project)).map_err(Box::new)?; + let tree = root_tree(&repo).map_err(Box::new)?; + let string = match read_file(&tree, [scope, name]) { + Ok(Some(s)) => s, + Ok(None) => return Ok(None), + Err(e) => { + return Err(errors::ReadIndexFileError::ReadFile(e)); + } + }; + + toml::from_str(&string).map_err(Into::into) + } } impl PackageSource for PesdePackageSource { @@ -121,23 +140,11 @@ impl PackageSource for PesdePackageSource { .. } = options; - let (scope, name) = specifier.name.as_str(); - let repo = gix::open(self.path(project)).map_err(Box::new)?; - let tree = root_tree(&repo).map_err(Box::new)?; - let string = match read_file(&tree, [scope, name]) { - Ok(Some(s)) => s, - Ok(None) => return Err(Self::ResolveError::NotFound(specifier.name.to_string())), - Err(e) => { - return Err(Self::ResolveError::Read( - specifier.name.to_string(), - Box::new(e), - )) - } + let Some(IndexFile { entries, .. }) = self.read_index_file(&specifier.name, project)? + else { + return Err(errors::ResolveError::NotFound(specifier.name.to_string())); }; - let IndexFile { entries, .. } = toml::from_str(&string) - .map_err(|e| Self::ResolveError::Parse(specifier.name.to_string(), e))?; - tracing::debug!("{} has {} possible entries", specifier.name, entries.len()); Ok(( @@ -149,16 +156,11 @@ impl PackageSource for PesdePackageSource { && specifier.target.unwrap_or(*project_target) == *target }) .map(|(id, entry)| { - let version = id.version().clone(); - ( id, PesdePackageRef { - name: specifier.name.clone(), - version, index_url: self.repo_url.clone(), dependencies: entry.dependencies, - target: entry.target, }, ) }) @@ -169,31 +171,28 @@ impl PackageSource for PesdePackageSource { #[instrument(skip_all, level = "debug")] async fn download( &self, - pkg_ref: &Self::Ref, + _pkg_ref: &Self::Ref, options: &DownloadOptions, ) -> Result { let DownloadOptions { project, reporter, reqwest, + id, + .. } = options; let config = self.config(project).await.map_err(Box::new)?; let index_file = project .cas_dir() .join("index") - .join(pkg_ref.name.escaped()) - .join(pkg_ref.version.to_string()) - .join(pkg_ref.target.to_string()); + .join(id.name().escaped()) + .join(id.version_id().version().to_string()) + .join(id.version_id().target().to_string()); match fs::read_to_string(&index_file).await { Ok(s) => { - tracing::debug!( - "using cached index file for package {}@{} {}", - pkg_ref.name, - pkg_ref.version, - pkg_ref.target - ); + tracing::debug!("using cached index file for package {id}"); reporter.report_done(); @@ -205,9 +204,9 @@ impl PackageSource for PesdePackageSource { let url = config .download() - .replace("{PACKAGE}", &pkg_ref.name.to_string().replace("/", "%2F")) - .replace("{PACKAGE_VERSION}", &pkg_ref.version.to_string()) - .replace("{PACKAGE_TARGET}", &pkg_ref.target.to_string()); + .replace("{PACKAGE}", &id.name().to_string().replace("/", "%2F")) + .replace("{PACKAGE_VERSION}", &id.version_id().version().to_string()) + .replace("{PACKAGE_TARGET}", &id.version_id().target().to_string()); let mut request = reqwest.get(&url).header(ACCEPT, "application/octet-stream"); @@ -294,10 +293,24 @@ impl PackageSource for PesdePackageSource { #[instrument(skip_all, level = "debug")] async fn get_target( &self, - pkg_ref: &Self::Ref, - _options: &GetTargetOptions, + _pkg_ref: &Self::Ref, + options: &GetTargetOptions, ) -> Result { - Ok(pkg_ref.target.clone()) + let GetTargetOptions { id, .. } = options; + let PackageNames::Pesde(name) = id.name() else { + panic!("unexpected package name"); + }; + + let Some(IndexFile { mut entries, .. }) = self.read_index_file(name, &options.project)? + else { + return Err(errors::GetTargetError::NotFound(name.to_string())); + }; + + let entry = entries + .remove(id.version_id()) + .ok_or_else(|| errors::GetTargetError::NotFound(name.to_string()))?; + + Ok(entry.target) } } @@ -496,10 +509,14 @@ pub mod errors { use crate::source::git_index::errors::{ReadFile, TreeError}; - /// Errors that can occur when resolving a package from a pesde package source + /// Errors that can occur when reading an index file of a pesde package source #[derive(Debug, Error)] #[non_exhaustive] - pub enum ResolveError { + pub enum ReadIndexFileError { + /// Error reading file + #[error("error reading file")] + ReadFile(#[from] ReadFile), + /// Error opening repository #[error("error opening repository")] Open(#[from] Box), @@ -508,17 +525,22 @@ pub mod errors { #[error("error getting tree")] Tree(#[from] Box), + /// Error parsing file + #[error("error parsing file")] + Parse(#[from] toml::de::Error), + } + + /// Errors that can occur when resolving a package from a pesde package source + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum ResolveError { /// Package not found in index #[error("package {0} not found")] NotFound(String), - /// Error reading file for package - #[error("error reading file for {0}")] - Read(String, #[source] Box), - - /// Error parsing file for package - #[error("error parsing file for {0}")] - Parse(String, #[source] toml::de::Error), + /// Error reading index file + #[error("error reading index file")] + ReadIndex(#[from] ReadIndexFileError), } /// Errors that can occur when reading the config file for a pesde package source @@ -586,5 +608,13 @@ pub mod errors { /// Errors that can occur when getting the target for a package from a pesde package source #[derive(Debug, Error)] #[non_exhaustive] - pub enum GetTargetError {} + pub enum GetTargetError { + /// Error reading index file + #[error("error reading index file")] + ReadIndex(#[from] ReadIndexFileError), + + /// Package not found in index + #[error("package `{0}` not found in index")] + NotFound(String), + } } diff --git a/src/source/pesde/pkg_ref.rs b/src/source/pesde/pkg_ref.rs index 14179ce..f66a764 100644 --- a/src/source/pesde/pkg_ref.rs +++ b/src/source/pesde/pkg_ref.rs @@ -1,21 +1,15 @@ use std::collections::BTreeMap; -use semver::Version; use serde::{Deserialize, Serialize}; use crate::{ - manifest::{target::Target, DependencyType}, - names::PackageName, + manifest::DependencyType, source::{pesde::PesdePackageSource, DependencySpecifiers, PackageRef, PackageSources}, }; /// A pesde package reference #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] pub struct PesdePackageRef { - /// The name of the package - pub name: PackageName, - /// The version of the package - pub version: Version, /// The index of the package #[serde( serialize_with = "crate::util::serialize_gix_url", @@ -25,8 +19,6 @@ pub struct PesdePackageRef { /// The dependencies of the package #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub dependencies: BTreeMap, - /// The target of the package - pub target: Target, } impl PackageRef for PesdePackageRef { fn dependencies(&self) -> &BTreeMap { diff --git a/src/source/traits.rs b/src/source/traits.rs index 74a3a8a..264ae70 100644 --- a/src/source/traits.rs +++ b/src/source/traits.rs @@ -4,7 +4,7 @@ use crate::{ DependencyType, }, reporters::DownloadProgressReporter, - source::{DependencySpecifiers, PackageFs, PackageSources, ResolveResult}, + source::{ids::PackageId, DependencySpecifiers, PackageFs, PackageSources, ResolveResult}, Project, RefreshedSources, }; use std::{ @@ -55,6 +55,8 @@ pub struct DownloadOptions { pub reqwest: reqwest::Client, /// The reporter to use pub reporter: Arc, + /// The package ID of the package to be downloaded + pub id: Arc, } /// Options for getting a package's Target @@ -64,6 +66,8 @@ pub struct GetTargetOptions { pub project: Project, /// The path the package has been written to pub path: Arc, + /// The package ID of the package to be downloaded + pub id: Arc, } /// A source of packages diff --git a/src/source/wally/compat_util.rs b/src/source/wally/compat_util.rs index 2a5275e..b5daf80 100644 --- a/src/source/wally/compat_util.rs +++ b/src/source/wally/compat_util.rs @@ -60,7 +60,7 @@ pub(crate) const WALLY_MANIFEST_FILE_NAME: &str = "wally.toml"; pub(crate) async fn get_target( options: &GetTargetOptions, ) -> Result { - let GetTargetOptions { project, path } = options; + let GetTargetOptions { project, path, .. } = options; let lib = find_lib_path(project, path) .await? diff --git a/src/source/wally/mod.rs b/src/source/wally/mod.rs index ffb9cc4..0d5faa0 100644 --- a/src/source/wally/mod.rs +++ b/src/source/wally/mod.rs @@ -185,21 +185,21 @@ impl PackageSource for WallyPackageSource { .into_iter() .filter(|manifest| specifier.version.matches(&manifest.package.version)) .map(|manifest| { + let dependencies = manifest.all_dependencies().map_err(|e| { + errors::ResolveError::AllDependencies(specifier.to_string(), e) + })?; + Ok(( VersionId( - manifest.package.version.clone(), + manifest.package.version, match manifest.package.realm { Realm::Server => TargetKind::RobloxServer, _ => TargetKind::Roblox, }, ), WallyPackageRef { - name: specifier.name.clone(), index_url: source.repo_url.clone(), - dependencies: manifest.all_dependencies().map_err(|e| { - errors::ResolveError::AllDependencies(specifier.to_string(), e) - })?, - version: manifest.package.version, + dependencies, }, )) }) @@ -213,29 +213,27 @@ impl PackageSource for WallyPackageSource { #[instrument(skip_all, level = "debug")] async fn download( &self, - pkg_ref: &Self::Ref, + _pkg_ref: &Self::Ref, options: &DownloadOptions, ) -> Result { let DownloadOptions { project, reqwest, reporter, + id, + .. } = options; let config = self.config(project).await.map_err(Box::new)?; let index_file = project .cas_dir() .join("wally_index") - .join(pkg_ref.name.escaped()) - .join(pkg_ref.version.to_string()); + .join(id.name().escaped()) + .join(id.version_id().version().to_string()); match fs::read_to_string(&index_file).await { Ok(s) => { - tracing::debug!( - "using cached index file for package {}@{}", - pkg_ref.name, - pkg_ref.version - ); + tracing::debug!("using cached index file for package {id}"); reporter.report_done(); @@ -245,13 +243,13 @@ impl PackageSource for WallyPackageSource { Err(e) => return Err(errors::DownloadError::ReadIndex(e)), }; - let (scope, name) = pkg_ref.name.as_str(); + let (scope, name) = id.name().as_str(); let mut request = reqwest .get(format!( "{}/v1/package-contents/{scope}/{name}/{}", config.api.as_str().trim_end_matches('/'), - pkg_ref.version + id.version_id().version() )) .header( "Wally-Version", diff --git a/src/source/wally/pkg_ref.rs b/src/source/wally/pkg_ref.rs index 08bdce9..69ae045 100644 --- a/src/source/wally/pkg_ref.rs +++ b/src/source/wally/pkg_ref.rs @@ -1,22 +1,15 @@ use std::collections::BTreeMap; -use semver::Version; use serde::{Deserialize, Serialize}; use crate::{ manifest::DependencyType, - names::wally::WallyPackageName, source::{wally::WallyPackageSource, DependencySpecifiers, PackageRef, PackageSources}, }; /// A Wally package reference #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] pub struct WallyPackageRef { - /// The name of the package - #[serde(rename = "wally")] - pub name: WallyPackageName, - /// The version of the package - pub version: Version, /// The index of the package #[serde( serialize_with = "crate::util::serialize_gix_url",