feat(manifest): add dependency names

This commit is contained in:
daimond113 2024-03-27 20:56:19 +01:00
parent 26da6abd22
commit ac48fbe18a
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
8 changed files with 124 additions and 97 deletions

View file

@ -84,7 +84,7 @@ pub async fn create_package(
let mut index = app_state.index.lock().unwrap(); let mut index = app_state.index.lock().unwrap();
let config = index.config()?; let config = index.config()?;
for (dependency, _) in manifest.dependencies() { for (dependency, _) in manifest.dependencies().into_values() {
match dependency { match dependency {
DependencySpecifier::Git(_) => { DependencySpecifier::Git(_) => {
if !config.git_allowed { if !config.git_allowed {

View file

@ -436,7 +436,7 @@ pub fn root_command(cmd: Command) -> anyhow::Result<()> {
} => { } => {
let mut manifest = project.manifest().clone(); let mut manifest = project.manifest().clone();
let specifier = match package.0 { let specifier = match package.0.clone() {
PackageName::Standard(name) => { PackageName::Standard(name) => {
DependencySpecifier::Registry(RegistryDependencySpecifier { DependencySpecifier::Registry(RegistryDependencySpecifier {
name, name,
@ -456,10 +456,32 @@ pub fn root_command(cmd: Command) -> anyhow::Result<()> {
), ),
}; };
fn insert_into(
deps: &mut BTreeMap<String, DependencySpecifier>,
specifier: DependencySpecifier,
name: PackageName,
) {
macro_rules! not_taken {
($key:expr) => {
(!deps.contains_key(&$key)).then_some($key)
};
}
let key = not_taken!(name.name().to_string())
.or_else(|| not_taken!(format!("{}/{}", name.scope(), name.name())))
.or_else(|| not_taken!(name.to_string()))
.unwrap();
deps.insert(key, specifier);
}
if peer { if peer {
manifest.peer_dependencies.push(specifier); insert_into(
&mut manifest.peer_dependencies,
specifier,
package.0.clone(),
);
} else { } else {
manifest.dependencies.push(specifier); insert_into(&mut manifest.dependencies, specifier, package.0.clone());
} }
serde_yaml::to_writer( serde_yaml::to_writer(
@ -471,7 +493,7 @@ pub fn root_command(cmd: Command) -> anyhow::Result<()> {
let mut manifest = project.manifest().clone(); let mut manifest = project.manifest().clone();
for dependencies in [&mut manifest.dependencies, &mut manifest.peer_dependencies] { for dependencies in [&mut manifest.dependencies, &mut manifest.peer_dependencies] {
dependencies.retain(|d| { dependencies.retain(|_, d| {
if let DependencySpecifier::Registry(registry) = d { if let DependencySpecifier::Registry(registry) = d {
match &package { match &package {
PackageName::Standard(name) => &registry.name != name, PackageName::Standard(name) => &registry.name != name,

View file

@ -1,5 +1,5 @@
use std::{ use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}, collections::{BTreeMap, HashMap, HashSet, VecDeque},
fmt::Display, fmt::Display,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -35,7 +35,7 @@ pub struct RootLockfileNode {
/// The specifiers of the root packages /// The specifiers of the root packages
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub specifiers: PackageMap<DependencySpecifier>, pub specifiers: PackageMap<(DependencySpecifier, String)>,
/// All nodes in the dependency graph /// All nodes in the dependency graph
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
@ -47,7 +47,7 @@ impl RootLockfileNode {
pub fn root_specifier( pub fn root_specifier(
&self, &self,
resolved_package: &ResolvedPackage, resolved_package: &ResolvedPackage,
) -> Option<&DependencySpecifier> { ) -> Option<&(DependencySpecifier, String)> {
self.specifiers self.specifiers
.get(&resolved_package.pkg_ref.name()) .get(&resolved_package.pkg_ref.name())
.and_then(|versions| versions.get(resolved_package.pkg_ref.version())) .and_then(|versions| versions.get(resolved_package.pkg_ref.version()))
@ -61,8 +61,8 @@ pub struct ResolvedPackage {
/// The reference to the package /// The reference to the package
pub pkg_ref: PackageRef, pub pkg_ref: PackageRef,
/// The dependencies of the package /// The dependencies of the package
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: BTreeSet<(PackageName, Version)>, pub dependencies: BTreeMap<PackageName, (Version, String)>,
/// The realm of the package /// The realm of the package
pub realm: Realm, pub realm: Realm,
/// The type of the dependency /// The type of the dependency
@ -212,7 +212,7 @@ impl Manifest {
root: &mut RootLockfileNode, root: &mut RootLockfileNode,
locked: bool, locked: bool,
project: &Project, project: &Project,
) -> Result<Vec<(DependencySpecifier, DependencyType)>, ResolveError> { ) -> Result<BTreeMap<String, (DependencySpecifier, DependencyType)>, ResolveError> {
Ok(if let Some(old_root) = project.lockfile()? { Ok(if let Some(old_root) = project.lockfile()? {
if self.overrides != old_root.overrides { if self.overrides != old_root.overrides {
// TODO: resolve only the changed dependencies (will this be worth it?) // TODO: resolve only the changed dependencies (will this be worth it?)
@ -221,12 +221,12 @@ impl Manifest {
} }
debug!("lockfile found, resolving dependencies from it"); debug!("lockfile found, resolving dependencies from it");
let mut missing = Vec::new(); let mut missing = BTreeMap::new();
let current_dependencies = self.dependencies(); let current_dependencies = self.dependencies();
let current_specifiers = current_dependencies let current_specifiers = current_dependencies
.iter() .values()
.map(|(d, _)| d) .map(|(specifier, _)| specifier)
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
// populate the new lockfile with all root dependencies (and their dependencies) from the old lockfile // populate the new lockfile with all root dependencies (and their dependencies) from the old lockfile
@ -234,7 +234,9 @@ impl Manifest {
for (version, resolved_package) in versions { for (version, resolved_package) in versions {
let specifier = old_root.root_specifier(resolved_package); let specifier = old_root.root_specifier(resolved_package);
if !specifier.is_some_and(|specifier| current_specifiers.contains(specifier)) { if !specifier
.is_some_and(|(specifier, _)| current_specifiers.contains(specifier))
{
continue; continue;
} }
@ -256,7 +258,7 @@ impl Manifest {
.or_default() .or_default()
.insert(version.clone(), resolved_package.clone()); .insert(version.clone(), resolved_package.clone());
for (dep_name, dep_version) in &resolved_package.dependencies { for (dep_name, (dep_version, _)) in &resolved_package.dependencies {
if root if root
.children .children
.get(dep_name) .get(dep_name)
@ -283,10 +285,11 @@ impl Manifest {
.specifiers .specifiers
.values() .values()
.flat_map(|v| v.values()) .flat_map(|v| v.values())
.map(|(specifier, _)| specifier)
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
// resolve new, or modified, dependencies from the manifest // resolve new, or modified, dependencies from the manifest
for (specifier, dep_type) in current_dependencies { for (desired_name, (specifier, dep_type)) in current_dependencies {
if old_specifiers.contains(&specifier) { if old_specifiers.contains(&specifier) {
continue; continue;
} }
@ -295,7 +298,7 @@ impl Manifest {
return Err(ResolveError::OutOfDateLockfile); return Err(ResolveError::OutOfDateLockfile);
} }
missing.push((specifier.clone(), dep_type)); missing.insert(desired_name, (specifier.clone(), dep_type));
} }
debug!( debug!(
@ -341,10 +344,13 @@ impl Manifest {
let mut queue = missing_dependencies let mut queue = missing_dependencies
.into_iter() .into_iter()
.map(|(specifier, dep_type)| (specifier, dep_type, None, vec![])) .map(|(desired_name, (specifier, dep_type))| {
(desired_name, specifier, dep_type, None, vec![])
})
.collect::<VecDeque<_>>(); .collect::<VecDeque<_>>();
while let Some((specifier, dep_type, dependant, mut path)) = queue.pop_front() { while let Some((desired_name, specifier, dep_type, dependant, mut path)) = queue.pop_front()
{
let depth = path.len(); let depth = path.len();
let (pkg_ref, default_realm, dependencies) = match &specifier { let (pkg_ref, default_realm, dependencies) = match &specifier {
@ -452,12 +458,15 @@ impl Manifest {
.and_then(|v| v.get_mut(&dependant_version)) .and_then(|v| v.get_mut(&dependant_version))
.unwrap() .unwrap()
.dependencies .dependencies
.insert((pkg_ref.name(), pkg_ref.version().clone())); .insert(
pkg_ref.name(),
(pkg_ref.version().clone(), desired_name.clone()),
);
} else { } else {
root.specifiers root.specifiers
.entry(pkg_ref.name()) .entry(pkg_ref.name())
.or_default() .or_default()
.insert(pkg_ref.version().clone(), specifier); .insert(pkg_ref.version().clone(), (specifier, desired_name.clone()));
} }
let resolved_versions = root.children.entry(pkg_ref.name()).or_default(); let resolved_versions = root.children.entry(pkg_ref.name()).or_default();
@ -503,7 +512,7 @@ impl Manifest {
pkg_ref.version().clone(), pkg_ref.version().clone(),
ResolvedPackage { ResolvedPackage {
pkg_ref: pkg_ref.clone(), pkg_ref: pkg_ref.clone(),
dependencies: BTreeSet::new(), dependencies: Default::default(),
realm: specifier_realm realm: specifier_realm
.unwrap_or_default() .unwrap_or_default()
.or(default_realm.unwrap_or_default()), .or(default_realm.unwrap_or_default()),
@ -511,15 +520,16 @@ impl Manifest {
}, },
); );
path.push(pkg_ref.name().to_string()); path.push(desired_name);
for (specifier, ty) in dependencies { for (desired_name, (specifier, ty)) in dependencies {
let overridden = overrides.iter().find_map(|(k_path, spec)| { let overridden = overrides.iter().find_map(|(k_path, spec)| {
(path == k_path[..k_path.len() - 1] && k_path.last() == Some(&specifier.name())) (path == k_path[..k_path.len() - 1] && k_path.last() == Some(&desired_name))
.then_some(spec) .then_some(spec)
}); });
queue.push_back(( queue.push_back((
desired_name,
overridden.cloned().unwrap_or(specifier), overridden.cloned().unwrap_or(specifier),
ty, ty,
Some((pkg_ref.name(), pkg_ref.version().clone())), Some((pkg_ref.name(), pkg_ref.version().clone())),
@ -541,7 +551,7 @@ impl Manifest {
let mut realm = resolved_package.realm; let mut realm = resolved_package.realm;
for (dep_name, dep_version) in &resolved_package.dependencies { for (dep_name, (dep_version, _)) in &resolved_package.dependencies {
let dep = root.children.get(dep_name).and_then(|v| v.get(dep_version)); let dep = root.children.get(dep_name).and_then(|v| v.get(dep_version));
if let Some(dep) = dep { if let Some(dep) = dep {

View file

@ -314,7 +314,7 @@ pub enum WallyManifestDependencyError {
pub(crate) fn parse_wally_dependencies( pub(crate) fn parse_wally_dependencies(
manifest: WallyManifest, manifest: WallyManifest,
) -> Result<Vec<DependencySpecifier>, WallyManifestDependencyError> { ) -> Result<BTreeMap<String, DependencySpecifier>, WallyManifestDependencyError> {
[ [
(manifest.dependencies, Realm::Shared), (manifest.dependencies, Realm::Shared),
(manifest.server_dependencies, Realm::Server), (manifest.server_dependencies, Realm::Server),
@ -322,22 +322,25 @@ pub(crate) fn parse_wally_dependencies(
] ]
.into_iter() .into_iter()
.flat_map(|(deps, realm)| { .flat_map(|(deps, realm)| {
deps.into_values() deps.into_iter()
.map(|specifier| { .map(move |(desired_name, specifier)| (desired_name, specifier, realm))
.map(|(desired_name, specifier, realm)| {
let (name, req) = specifier.split_once('@').ok_or_else(|| { let (name, req) = specifier.split_once('@').ok_or_else(|| {
WallyManifestDependencyError::InvalidDependencySpecifier(specifier.clone()) WallyManifestDependencyError::InvalidDependencySpecifier(specifier.clone())
})?; })?;
let name: WallyPackageName = name.parse()?; let name: WallyPackageName = name.parse()?;
let req: VersionReq = req.parse()?; let req: VersionReq = req.parse()?;
Ok(DependencySpecifier::Wally(WallyDependencySpecifier { Ok((
name, desired_name,
version: req, DependencySpecifier::Wally(WallyDependencySpecifier {
index_url: manifest.package.registry.clone(), name,
realm: Some(realm), version: req,
})) index_url: manifest.package.registry.clone(),
realm: Some(realm),
}),
))
}) })
.collect::<Vec<_>>()
}) })
.collect() .collect()
} }
@ -348,7 +351,7 @@ impl TryFrom<WallyManifest> for IndexFileEntry {
fn try_from(value: WallyManifest) -> Result<Self, Self::Error> { fn try_from(value: WallyManifest) -> Result<Self, Self::Error> {
let dependencies = parse_wally_dependencies(value.clone())? let dependencies = parse_wally_dependencies(value.clone())?
.into_iter() .into_iter()
.map(|d| (d, DependencyType::Normal)) .map(|(desired_name, specifier)| (desired_name, (specifier, DependencyType::Normal)))
.collect(); .collect();
Ok(IndexFileEntry { Ok(IndexFileEntry {

View file

@ -1,6 +1,6 @@
use std::{ use std::{
any::Any, any::Any,
collections::BTreeSet, collections::{BTreeMap, BTreeSet},
fmt::Debug, fmt::Debug,
fs::create_dir_all, fs::create_dir_all,
hash::Hash, hash::Hash,
@ -578,8 +578,8 @@ pub struct IndexFileEntry {
pub description: Option<String>, pub description: Option<String>,
/// The dependencies of the package /// The dependencies of the package
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: Vec<(DependencySpecifier, DependencyType)>, pub dependencies: BTreeMap<String, (DependencySpecifier, DependencyType)>,
} }
/// An error that occurred while converting a manifest to an index file entry /// An error that occurred while converting a manifest to an index file entry
@ -606,21 +606,24 @@ impl TryFrom<Manifest> for IndexFileEntry {
dependencies: dependencies dependencies: dependencies
.into_iter() .into_iter()
.map(|(dep, ty)| { .map(|(desired_name, (dep, ty))| {
Ok(match dep { Ok((
DependencySpecifier::Registry(mut registry) => { desired_name,
registry.index = indices match dep {
.get(&registry.index) DependencySpecifier::Registry(mut registry) => {
.ok_or_else(|| { registry.index = indices
FromManifestIndexFileEntry::IndexNotSpecified( .get(&registry.index)
registry.index.clone(), .ok_or_else(|| {
) FromManifestIndexFileEntry::IndexNotSpecified(
})? registry.index.clone(),
.clone(); )
(DependencySpecifier::Registry(registry), ty) })?
} .clone();
d => (d, ty), (DependencySpecifier::Registry(registry), ty)
}) }
d => (d, ty),
},
))
}) })
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
}) })

View file

@ -128,7 +128,7 @@ pub(crate) fn link<P: AsRef<Path>, Q: AsRef<Path>>(
lockfile: &RootLockfileNode, lockfile: &RootLockfileNode,
destination_dir: P, destination_dir: P,
parent_dependency_packages_dir: Q, parent_dependency_packages_dir: Q,
only_name: bool, desired_name: &str,
as_root: bool, as_root: bool,
) -> Result<(), LinkingError> { ) -> Result<(), LinkingError> {
let (_, source_dir) = resolved_pkg.directory(project.path()); let (_, source_dir) = resolved_pkg.directory(project.path());
@ -153,17 +153,13 @@ pub(crate) fn link<P: AsRef<Path>, Q: AsRef<Path>>(
.get(&pkg_name) .get(&pkg_name)
.and_then(|v| v.get(resolved_pkg.pkg_ref.version())) .and_then(|v| v.get(resolved_pkg.pkg_ref.version()))
{ {
Some(specifier) if as_root => project.path().join(packages_folder( Some((specifier, _)) if as_root => project.path().join(packages_folder(
specifier.realm().copied().unwrap_or_default(), specifier.realm().copied().unwrap_or_default(),
)), )),
_ => destination_dir.as_ref().to_path_buf(), _ => destination_dir.as_ref().to_path_buf(),
}; };
let destination_file = destination_dir.join(format!( let destination_file = destination_dir.join(desired_name.to_string() + ".lua");
"{}{}.lua",
if only_name { "" } else { pkg_name.prefix() },
name
));
let realm_folder = project.path().join(resolved_pkg.packages_folder()); let realm_folder = project.path().join(resolved_pkg.packages_folder());
let in_different_folders = realm_folder != parent_dependency_packages_dir.as_ref(); let in_different_folders = realm_folder != parent_dependency_packages_dir.as_ref();
@ -235,16 +231,6 @@ pub struct LinkingDependenciesError(
Version, Version,
); );
fn is_duplicate_in<T: PartialEq>(item: T, items: &[T]) -> bool {
let mut count = 0u8;
items.iter().any(|i| {
if i == &item {
count += 1;
}
count > 1
})
}
impl Project { impl Project {
/// Links the dependencies of the project /// Links the dependencies of the project
pub fn link_dependencies( pub fn link_dependencies(
@ -252,7 +238,6 @@ impl Project {
lockfile: &RootLockfileNode, lockfile: &RootLockfileNode,
) -> Result<(), LinkingDependenciesError> { ) -> Result<(), LinkingDependenciesError> {
let root_deps = lockfile.specifiers.keys().collect::<HashSet<_>>(); let root_deps = lockfile.specifiers.keys().collect::<HashSet<_>>();
let root_dep_names = root_deps.iter().map(|n| n.name()).collect::<Vec<_>>();
for (name, versions) in &lockfile.children { for (name, versions) in &lockfile.children {
for (version, resolved_pkg) in versions { for (version, resolved_pkg) in versions {
@ -263,13 +248,7 @@ impl Project {
container_dir.display() container_dir.display()
); );
let resolved_pkg_dep_names = resolved_pkg for (dep_name, (dep_version, desired_name)) in &resolved_pkg.dependencies {
.dependencies
.iter()
.map(|(n, _)| n.name())
.collect::<Vec<_>>();
for (dep_name, dep_version) in &resolved_pkg.dependencies {
let dep = lockfile let dep = lockfile
.children .children
.get(dep_name) .get(dep_name)
@ -282,7 +261,7 @@ impl Project {
lockfile, lockfile,
&container_dir, &container_dir,
&self.path().join(resolved_pkg.packages_folder()), &self.path().join(resolved_pkg.packages_folder()),
!is_duplicate_in(dep_name.name(), &resolved_pkg_dep_names), desired_name,
false, false,
) )
.map_err(|e| { .map_err(|e| {
@ -297,7 +276,7 @@ impl Project {
} }
if root_deps.contains(&name) { if root_deps.contains(&name) {
let specifier = lockfile.root_specifier(resolved_pkg).unwrap(); let (specifier, desired_name) = lockfile.root_specifier(resolved_pkg).unwrap();
let linking_dir = &self.path().join(packages_folder( let linking_dir = &self.path().join(packages_folder(
specifier.realm().copied().unwrap_or_default(), specifier.realm().copied().unwrap_or_default(),
)); ));
@ -313,7 +292,7 @@ impl Project {
lockfile, lockfile,
linking_dir, linking_dir,
self.path().join(resolved_pkg.packages_folder()), self.path().join(resolved_pkg.packages_folder()),
!is_duplicate_in(name.name(), &root_dep_names), desired_name,
true, true,
) )
.map_err(|e| { .map_err(|e| {

View file

@ -189,11 +189,11 @@ pub struct Manifest {
pub overrides: BTreeMap<OverrideKey, DependencySpecifier>, pub overrides: BTreeMap<OverrideKey, DependencySpecifier>,
/// The dependencies of the package /// The dependencies of the package
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: Vec<DependencySpecifier>, pub dependencies: BTreeMap<String, DependencySpecifier>,
/// The peer dependencies of the package /// The peer dependencies of the package
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub peer_dependencies: Vec<DependencySpecifier>, pub peer_dependencies: BTreeMap<String, DependencySpecifier>,
} }
/// An error that occurred while reading the manifest /// An error that occurred while reading the manifest
@ -338,7 +338,7 @@ impl Manifest {
overrides: BTreeMap::new(), overrides: BTreeMap::new(),
dependencies, dependencies,
peer_dependencies: Vec::new(), peer_dependencies: Default::default(),
description: wally_manifest.package.description, description: wally_manifest.package.description,
license: wally_manifest.package.license, license: wally_manifest.package.license,
authors: wally_manifest.package.authors, authors: wally_manifest.package.authors,
@ -361,14 +361,24 @@ impl Manifest {
} }
/// Returns all dependencies /// Returns all dependencies
pub fn dependencies(&self) -> Vec<(DependencySpecifier, DependencyType)> { pub fn dependencies(&self) -> BTreeMap<String, (DependencySpecifier, DependencyType)> {
self.dependencies self.dependencies
.iter() .iter()
.map(|dep| (dep.clone(), DependencyType::Normal)) .map(|(desired_name, specifier)| {
(
desired_name.clone(),
(specifier.clone(), DependencyType::Normal),
)
})
.chain( .chain(
self.peer_dependencies self.peer_dependencies
.iter() .iter()
.map(|dep| (dep.clone(), DependencyType::Peer)), .map(|(desired_name, specifier)| {
(
desired_name.clone(),
(specifier.clone(), DependencyType::Peer),
)
}),
) )
.collect() .collect()
} }

View file

@ -1,4 +1,4 @@
use std::collections::{BTreeSet, HashMap}; use std::collections::{BTreeMap, BTreeSet, HashMap};
use semver::Version; use semver::Version;
use tempfile::tempdir; use tempfile::tempdir;
@ -45,8 +45,8 @@ fn test_resolves_package() {
sourcemap_generator: None, sourcemap_generator: None,
overrides: Default::default(), overrides: Default::default(),
dependencies: vec![], dependencies: Default::default(),
peer_dependencies: vec![], peer_dependencies: Default::default(),
description: Some(description.to_string()), description: Some(description.to_string()),
license: None, license: None,
authors: None, authors: None,
@ -86,8 +86,8 @@ fn test_resolves_package() {
sourcemap_generator: None, sourcemap_generator: None,
overrides: Default::default(), overrides: Default::default(),
dependencies: vec![specifier.clone()], dependencies: BTreeMap::from([("test".to_string(), specifier.clone())]),
peer_dependencies: vec![specifier_2.clone()], peer_dependencies: BTreeMap::from([("test2".to_string(), specifier_2.clone())]),
description: Some(description.to_string()), description: Some(description.to_string()),
license: None, license: None,
authors: None, authors: None,