feat: implement multi target packages

This commit is contained in:
daimond113 2024-07-22 23:16:04 +02:00
parent 14463b7205
commit 2898b02e1c
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
10 changed files with 148 additions and 117 deletions

View file

@ -63,6 +63,7 @@ impl InstallCommand {
.write_lockfile(Lockfile { .write_lockfile(Lockfile {
name: manifest.name, name: manifest.name,
version: manifest.version, version: manifest.version,
target: manifest.target.kind(),
overrides: manifest.overrides, overrides: manifest.overrides,
graph: downloaded_graph, graph: downloaded_graph,

View file

@ -245,9 +245,16 @@ impl IsUpToDate for Project {
Err(e) => return Err(e.into()), Err(e) => return Err(e.into()),
}; };
if manifest.overrides != lockfile.overrides {
return Ok(false);
}
if manifest.target.kind() != lockfile.target {
return Ok(false);
}
if !strict { if !strict {
// the resolver will use the old lockfile and update it itself. it can't handle overrides only return Ok(true);
return Ok(manifest.overrides == lockfile.overrides);
} }
if manifest.name != lockfile.name || manifest.version != lockfile.version { if manifest.name != lockfile.name || manifest.version != lockfile.version {

View file

@ -30,7 +30,7 @@ impl RunCommand {
let pkg_name = PackageNames::Pesde(pkg_name); let pkg_name = PackageNames::Pesde(pkg_name);
for (version, node) in graph.get(&pkg_name).context("package not found in graph")? { for (version_id, node) in graph.get(&pkg_name).context("package not found in graph")? {
if node.node.direct.is_none() { if node.node.direct.is_none() {
continue; continue;
} }
@ -48,7 +48,7 @@ impl RunCommand {
.join(base_folder) .join(base_folder)
.join(PACKAGES_CONTAINER_NAME), .join(PACKAGES_CONTAINER_NAME),
&pkg_name, &pkg_name,
version, version_id.version(),
); );
let path = bin_path.to_path(&container_folder); let path = bin_path.to_path(&container_folder);

View file

@ -21,7 +21,7 @@ impl Project {
let mut downloaded_graph: DownloadedGraph = BTreeMap::new(); let mut downloaded_graph: DownloadedGraph = BTreeMap::new();
for (name, versions) in graph { for (name, versions) in graph {
for (version, node) in versions { for (version_id, node) in versions {
let source = match &node.pkg_ref { let source = match &node.pkg_ref {
PackageRefs::Pesde(pkg_ref) => { PackageRefs::Pesde(pkg_ref) => {
PackageSources::Pesde(PesdePackageSource::new(pkg_ref.index_url.clone())) PackageSources::Pesde(PesdePackageSource::new(pkg_ref.index_url.clone()))
@ -38,7 +38,7 @@ impl Project {
.join(node.base_folder(manifest.target.kind(), true)) .join(node.base_folder(manifest.target.kind(), true))
.join(PACKAGES_CONTAINER_NAME), .join(PACKAGES_CONTAINER_NAME),
name, name,
version, version_id.version(),
); );
create_dir_all(&container_folder)?; create_dir_all(&container_folder)?;
@ -46,7 +46,7 @@ impl Project {
let target = source.download(&node.pkg_ref, &container_folder, self)?; let target = source.download(&node.pkg_ref, &container_folder, self)?;
downloaded_graph.entry(name.clone()).or_default().insert( downloaded_graph.entry(name.clone()).or_default().insert(
version.clone(), version_id.clone(),
DownloadedDependencyGraphNode { DownloadedDependencyGraphNode {
node: node.clone(), node: node.clone(),
target, target,

View file

@ -4,10 +4,9 @@ use crate::{
manifest::{ScriptName, Target}, manifest::{ScriptName, Target},
names::PackageNames, names::PackageNames,
scripts::execute_script, scripts::execute_script,
source::PackageRef, source::{PackageRef, VersionId},
Project, PACKAGES_CONTAINER_NAME, Project, PACKAGES_CONTAINER_NAME,
}; };
use semver::Version;
use std::{collections::BTreeMap, fs::create_dir_all}; use std::{collections::BTreeMap, fs::create_dir_all};
pub mod generator; pub mod generator;
@ -16,10 +15,10 @@ impl Project {
pub fn link_dependencies(&self, graph: &DownloadedGraph) -> Result<(), errors::LinkingError> { pub fn link_dependencies(&self, graph: &DownloadedGraph) -> Result<(), errors::LinkingError> {
let manifest = self.deser_manifest()?; let manifest = self.deser_manifest()?;
let mut package_types = BTreeMap::<&PackageNames, BTreeMap<&Version, Vec<String>>>::new(); let mut package_types = BTreeMap::<&PackageNames, BTreeMap<&VersionId, Vec<String>>>::new();
for (name, versions) in graph { for (name, versions) in graph {
for (version, node) in versions { for (version_id, node) in versions {
let Some(lib_file) = node.target.lib_path() else { let Some(lib_file) = node.target.lib_path() else {
continue; continue;
}; };
@ -30,7 +29,7 @@ impl Project {
.join(node.node.base_folder(manifest.target.kind(), true)) .join(node.node.base_folder(manifest.target.kind(), true))
.join(PACKAGES_CONTAINER_NAME), .join(PACKAGES_CONTAINER_NAME),
name, name,
version, version_id.version(),
); );
let lib_file = lib_file.to_path(&container_folder); let lib_file = lib_file.to_path(&container_folder);
@ -58,7 +57,7 @@ impl Project {
package_types package_types
.entry(name) .entry(name)
.or_default() .or_default()
.insert(version, types); .insert(version_id, types);
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
if let Target::Roblox { build_files, .. } = &node.target { if let Target::Roblox { build_files, .. } = &node.target {
@ -87,7 +86,7 @@ impl Project {
} }
for (name, versions) in graph { for (name, versions) in graph {
for (version, node) in versions { for (version_id, node) in versions {
let base_folder = self.path().join( let base_folder = self.path().join(
self.path() self.path()
.join(node.node.base_folder(manifest.target.kind(), true)), .join(node.node.base_folder(manifest.target.kind(), true)),
@ -96,13 +95,15 @@ impl Project {
let base_folder = base_folder.canonicalize()?; let base_folder = base_folder.canonicalize()?;
let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME); let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME);
let container_folder = let container_folder = node.node.container_folder(
node.node &packages_container_folder,
.container_folder(&packages_container_folder, name, version); name,
version_id.version(),
);
if let Some((alias, types)) = package_types if let Some((alias, types)) = package_types
.get(name) .get(name)
.and_then(|v| v.get(version)) .and_then(|v| v.get(version_id))
.and_then(|types| node.node.direct.as_ref().map(|(alias, _)| (alias, types))) .and_then(|types| node.node.direct.as_ref().map(|(alias, _)| (alias, types)))
{ {
let module = generator::generate_linking_module( let module = generator::generate_linking_module(
@ -118,23 +119,23 @@ impl Project {
std::fs::write(base_folder.join(format!("{alias}.luau")), module)?; std::fs::write(base_folder.join(format!("{alias}.luau")), module)?;
} }
for (dependency_name, (dependency_version, dependency_alias)) in for (dependency_name, (dependency_version_id, dependency_alias)) in
&node.node.dependencies &node.node.dependencies
{ {
let Some(dependency_node) = graph let Some(dependency_node) = graph
.get(dependency_name) .get(dependency_name)
.and_then(|v| v.get(dependency_version)) .and_then(|v| v.get(dependency_version_id))
else { else {
return Err(errors::LinkingError::DependencyNotFound( return Err(errors::LinkingError::DependencyNotFound(
dependency_name.to_string(), dependency_name.to_string(),
dependency_version.to_string(), dependency_version_id.to_string(),
)); ));
}; };
let dependency_container_folder = dependency_node.node.container_folder( let dependency_container_folder = dependency_node.node.container_folder(
&packages_container_folder, &packages_container_folder,
dependency_name, dependency_name,
dependency_version, dependency_version_id.version(),
); );
let linker_folder = container_folder let linker_folder = container_folder
@ -153,7 +154,7 @@ impl Project {
)?, )?,
package_types package_types
.get(dependency_name) .get(dependency_name)
.and_then(|v| v.get(dependency_version)) .and_then(|v| v.get(dependency_version_id))
.unwrap(), .unwrap(),
); );

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
manifest::{DependencyType, OverrideKey, Target, TargetKind}, manifest::{DependencyType, OverrideKey, Target, TargetKind},
names::{PackageName, PackageNames}, names::{PackageName, PackageNames},
source::{DependencySpecifiers, PackageRef, PackageRefs}, source::{DependencySpecifiers, PackageRef, PackageRefs, VersionId},
}; };
use semver::Version; use semver::Version;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -10,16 +10,16 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
pub type Graph<Node> = BTreeMap<PackageNames, BTreeMap<Version, Node>>; pub type Graph<Node> = BTreeMap<PackageNames, BTreeMap<VersionId, Node>>;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DependencyGraphNode { pub struct DependencyGraphNode {
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub direct: Option<(String, DependencySpecifiers)>, pub direct: Option<(String, DependencySpecifiers)>,
pub pkg_ref: PackageRefs,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: BTreeMap<PackageNames, (Version, String)>, pub dependencies: BTreeMap<PackageNames, (VersionId, String)>,
pub ty: DependencyType, pub ty: DependencyType,
pub pkg_ref: PackageRefs,
} }
impl DependencyGraphNode { impl DependencyGraphNode {
@ -49,7 +49,7 @@ pub type DependencyGraph = Graph<DependencyGraphNode>;
pub fn insert_node( pub fn insert_node(
graph: &mut DependencyGraph, graph: &mut DependencyGraph,
name: PackageNames, name: PackageNames,
version: Version, version: VersionId,
mut node: DependencyGraphNode, mut node: DependencyGraphNode,
is_top_level: bool, is_top_level: bool,
) { ) {
@ -85,8 +85,9 @@ pub fn insert_node(
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DownloadedDependencyGraphNode { pub struct DownloadedDependencyGraphNode {
pub node: DependencyGraphNode,
pub target: Target, pub target: Target,
#[serde(flatten)]
pub node: DependencyGraphNode,
} }
pub type DownloadedGraph = Graph<DownloadedDependencyGraphNode>; pub type DownloadedGraph = Graph<DownloadedDependencyGraphNode>;
@ -95,6 +96,7 @@ pub type DownloadedGraph = Graph<DownloadedDependencyGraphNode>;
pub struct Lockfile { pub struct Lockfile {
pub name: PackageName, pub name: PackageName,
pub version: Version, pub version: Version,
pub target: TargetKind,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>, pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>,

View file

@ -4,14 +4,13 @@ use crate::{
names::PackageNames, names::PackageNames,
source::{ source::{
pesde::PesdePackageSource, DependencySpecifiers, PackageRef, PackageSource, PackageSources, pesde::PesdePackageSource, DependencySpecifiers, PackageRef, PackageSource, PackageSources,
VersionId,
}, },
Project, DEFAULT_INDEX_NAME, Project, DEFAULT_INDEX_NAME,
}; };
use semver::Version;
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
impl Project { impl Project {
// TODO: account for targets using the is_compatible_with method
pub fn dependency_graph( pub fn dependency_graph(
&self, &self,
previous_graph: Option<&DependencyGraph>, previous_graph: Option<&DependencyGraph>,
@ -106,14 +105,17 @@ impl Project {
alias.to_string(), alias.to_string(),
spec, spec,
ty, ty,
None::<(PackageNames, Version)>, None::<(PackageNames, VersionId)>,
vec![alias.to_string()], vec![alias.to_string()],
false, false,
manifest.target.kind(),
) )
}) })
.collect::<VecDeque<_>>(); .collect::<VecDeque<_>>();
while let Some((alias, specifier, ty, dependant, path, overridden)) = queue.pop_front() { while let Some((alias, specifier, ty, dependant, path, overridden, target)) =
queue.pop_front()
{
let depth = path.len() - 1; let depth = path.len() - 1;
log::debug!( log::debug!(
@ -155,10 +157,10 @@ impl Project {
} }
let (name, resolved) = source let (name, resolved) = source
.resolve(&specifier, self) .resolve(&specifier, self, target)
.map_err(|e| Box::new(e.into()))?; .map_err(|e| Box::new(e.into()))?;
let Some(target_version) = graph let Some(target_version_id) = graph
.get(&name) .get(&name)
.and_then(|versions| { .and_then(|versions| {
versions versions
@ -170,11 +172,9 @@ impl Project {
.or_else(|| resolved.last_key_value().map(|(ver, _)| ver)) .or_else(|| resolved.last_key_value().map(|(ver, _)| ver))
.cloned() .cloned()
else { else {
log::warn!( return Err(Box::new(errors::DependencyGraphError::NoMatchingVersion(
"{}could not find any version for {specifier} ({alias})", format!("{specifier} ({})", manifest.target.kind()),
"\t".repeat(depth) )));
);
continue;
}; };
let ty = if depth == 0 && ty == DependencyType::Peer { let ty = if depth == 0 && ty == DependencyType::Peer {
@ -183,25 +183,25 @@ impl Project {
ty ty
}; };
if let Some((dependant_name, dependant_version)) = dependant { if let Some((dependant_name, dependant_version_id)) = dependant {
graph graph
.get_mut(&dependant_name) .get_mut(&dependant_name)
.and_then(|versions| versions.get_mut(&dependant_version)) .and_then(|versions| versions.get_mut(&dependant_version_id))
.and_then(|node| { .and_then(|node| {
node.dependencies node.dependencies
.insert(name.clone(), (target_version.clone(), alias.clone())) .insert(name.clone(), (target_version_id.clone(), alias.clone()))
}); });
} }
if let Some(already_resolved) = graph if let Some(already_resolved) = graph
.get_mut(&name) .get_mut(&name)
.and_then(|versions| versions.get_mut(&target_version)) .and_then(|versions| versions.get_mut(&target_version_id))
{ {
log::debug!( log::debug!(
"{}{}@{} already resolved", "{}{}@{} already resolved",
"\t".repeat(depth), "\t".repeat(depth),
name, name,
target_version target_version_id
); );
if already_resolved.ty == DependencyType::Peer && ty == DependencyType::Standard { if already_resolved.ty == DependencyType::Peer && ty == DependencyType::Standard {
@ -211,7 +211,7 @@ impl Project {
continue; continue;
} }
let pkg_ref = &resolved[&target_version]; let pkg_ref = &resolved[&target_version_id];
let node = DependencyGraphNode { let node = DependencyGraphNode {
direct: if depth == 0 { direct: if depth == 0 {
Some((alias.clone(), specifier.clone())) Some((alias.clone(), specifier.clone()))
@ -225,7 +225,7 @@ impl Project {
insert_node( insert_node(
&mut graph, &mut graph,
name.clone(), name.clone(),
target_version.clone(), target_version_id.clone(),
node.clone(), node.clone(),
depth == 0, depth == 0,
); );
@ -234,7 +234,7 @@ impl Project {
"{}resolved {}@{} from new dependency graph", "{}resolved {}@{} from new dependency graph",
"\t".repeat(depth), "\t".repeat(depth),
name, name,
target_version target_version_id
); );
for (dependency_alias, (dependency_spec, dependency_ty)) in for (dependency_alias, (dependency_spec, dependency_ty)) in
@ -262,20 +262,21 @@ impl Project {
dependency_alias, dependency_alias,
overridden.cloned().unwrap_or(dependency_spec), overridden.cloned().unwrap_or(dependency_spec),
dependency_ty, dependency_ty,
Some((name.clone(), target_version.clone())), Some((name.clone(), target_version_id.clone())),
path.iter() path.iter()
.cloned() .cloned()
.chain(std::iter::once(alias.to_string())) .chain(std::iter::once(alias.to_string()))
.collect(), .collect(),
overridden.is_some(), overridden.is_some(),
pkg_ref.target_kind(),
)); ));
} }
} }
for (name, versions) in &graph { for (name, versions) in &graph {
for (version, node) in versions { for (version_id, node) in versions {
if node.ty == DependencyType::Peer { if node.ty == DependencyType::Peer {
log::warn!("peer dependency {name}@{version} was not resolved"); log::warn!("peer dependency {name}@{version_id} was not resolved");
} }
} }
} }
@ -310,5 +311,8 @@ pub mod errors {
#[error("error resolving package")] #[error("error resolving package")]
Resolve(#[from] crate::source::errors::ResolveError), Resolve(#[from] crate::source::errors::ResolveError),
#[error("no matching version found for {0}")]
NoMatchingVersion(String),
} }
} }

View file

@ -1,17 +1,17 @@
use std::{
collections::BTreeMap,
fmt::{Debug, Display},
path::Path,
};
use semver::Version;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
manifest::{DependencyType, Target, TargetKind}, manifest::{DependencyType, Target, TargetKind},
names::PackageNames, names::PackageNames,
Project, Project,
}; };
use semver::Version;
use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::{
collections::BTreeMap,
fmt::{Debug, Display},
path::Path,
str::FromStr,
};
pub mod pesde; pub mod pesde;
@ -40,6 +40,7 @@ impl Display for DependencySpecifiers {
} }
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "snake_case", tag = "ref_ty")]
pub enum PackageRefs { pub enum PackageRefs {
Pesde(pesde::pkg_ref::PesdePackageRef), Pesde(pesde::pkg_ref::PesdePackageRef),
} }
@ -68,7 +69,43 @@ impl PackageRef for PackageRefs {
} }
} }
pub type ResolveResult<Ref> = (PackageNames, BTreeMap<Version, Ref>); #[derive(
Debug, SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
)]
pub struct VersionId(Version, TargetKind);
impl VersionId {
pub fn version(&self) -> &Version {
&self.0
}
pub fn target(&self) -> &TargetKind {
&self.1
}
}
impl Display for VersionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.0, self.1)
}
}
impl FromStr for VersionId {
type Err = errors::VersionIdParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let Some((version, target)) = s.split_once(' ') else {
return Err(errors::VersionIdParseError::Malformed(s.to_string()));
};
let version = version.parse()?;
let target = target.parse()?;
Ok(VersionId(version, target))
}
}
pub type ResolveResult<Ref> = (PackageNames, BTreeMap<VersionId, Ref>);
#[derive(Debug, Eq, PartialEq, Hash, Clone)] #[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub enum PackageSources { pub enum PackageSources {
@ -89,6 +126,7 @@ pub trait PackageSource: Debug {
&self, &self,
specifier: &Self::Specifier, specifier: &Self::Specifier,
project: &Project, project: &Project,
project_target: TargetKind,
) -> Result<ResolveResult<Self::Ref>, Self::ResolveError>; ) -> Result<ResolveResult<Self::Ref>, Self::ResolveError>;
fn download( fn download(
@ -115,10 +153,11 @@ impl PackageSource for PackageSources {
&self, &self,
specifier: &Self::Specifier, specifier: &Self::Specifier,
project: &Project, project: &Project,
project_target: TargetKind,
) -> Result<ResolveResult<Self::Ref>, Self::ResolveError> { ) -> Result<ResolveResult<Self::Ref>, Self::ResolveError> {
match (self, specifier) { match (self, specifier) {
(PackageSources::Pesde(source), DependencySpecifiers::Pesde(specifier)) => source (PackageSources::Pesde(source), DependencySpecifiers::Pesde(specifier)) => source
.resolve(specifier, project) .resolve(specifier, project, project_target)
.map(|(name, results)| { .map(|(name, results)| {
( (
name, name,
@ -179,4 +218,17 @@ pub mod errors {
#[error("error downloading pesde package")] #[error("error downloading pesde package")]
Pesde(#[from] crate::source::pesde::errors::DownloadError), Pesde(#[from] crate::source::pesde::errors::DownloadError),
} }
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum VersionIdParseError {
#[error("malformed entry key {0}")]
Malformed(String),
#[error("malformed version")]
Version(#[from] semver::Error),
#[error("malformed target")]
Target(#[from] crate::manifest::errors::TargetKindFromStr),
}
} }

View file

@ -1,23 +1,16 @@
use std::{ use std::{collections::BTreeMap, fmt::Debug, hash::Hash, path::Path};
collections::BTreeMap,
fmt::{Debug, Display},
hash::Hash,
path::Path,
str::FromStr,
};
use gix::remote::Direction; use gix::remote::Direction;
use semver::Version;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use pkg_ref::PesdePackageRef; use pkg_ref::PesdePackageRef;
use specifier::PesdeDependencySpecifier; use specifier::PesdeDependencySpecifier;
use crate::manifest::TargetKind;
use crate::{ use crate::{
manifest::{DependencyType, Target, TargetKind}, manifest::{DependencyType, Target},
names::{PackageName, PackageNames}, names::{PackageName, PackageNames},
source::{hash, DependencySpecifiers, PackageSource, ResolveResult}, source::{hash, DependencySpecifiers, PackageSource, ResolveResult, VersionId},
util::authenticate_conn, util::authenticate_conn,
Project, REQWEST_CLIENT, Project, REQWEST_CLIENT,
}; };
@ -307,6 +300,7 @@ impl PackageSource for PesdePackageSource {
&self, &self,
specifier: &Self::Specifier, specifier: &Self::Specifier,
project: &Project, project: &Project,
project_target: TargetKind,
) -> Result<ResolveResult<Self::Ref>, Self::ResolveError> { ) -> Result<ResolveResult<Self::Ref>, Self::ResolveError> {
let (scope, name) = specifier.name.as_str(); let (scope, name) = specifier.name.as_str();
let string = match self.read_file([scope, name], project) { let string = match self.read_file([scope, name], project) {
@ -322,10 +316,17 @@ impl PackageSource for PesdePackageSource {
PackageNames::Pesde(specifier.name.clone()), PackageNames::Pesde(specifier.name.clone()),
entries entries
.into_iter() .into_iter()
.filter(|(EntryKey(version, _), _)| specifier.version.matches(version)) .filter(|(VersionId(version, target), _)| {
.map(|(EntryKey(version, _), entry)| { specifier.version.matches(version)
&& specifier
.target
.map_or(project_target.is_compatible_with(target), |t| t == *target)
})
.map(|(id, entry)| {
let version = id.version().clone();
( (
version.clone(), id,
PesdePackageRef { PesdePackageRef {
name: specifier.name.clone(), name: specifier.name.clone(),
version, version,
@ -413,33 +414,7 @@ pub struct IndexFileEntry {
pub dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>, pub dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>,
} }
#[derive( pub type IndexFile = BTreeMap<VersionId, IndexFileEntry>;
Debug, SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
)]
pub struct EntryKey(pub Version, pub TargetKind);
impl Display for EntryKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.0, self.1)
}
}
impl FromStr for EntryKey {
type Err = errors::EntryKeyParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let Some((version, target)) = s.split_once(' ') else {
return Err(errors::EntryKeyParseError::Malformed(s.to_string()));
};
let version = version.parse()?;
let target = target.parse()?;
Ok(EntryKey(version, target))
}
}
pub type IndexFile = BTreeMap<EntryKey, IndexFileEntry>;
pub mod errors { pub mod errors {
use std::path::PathBuf; use std::path::PathBuf;
@ -593,17 +568,4 @@ pub mod errors {
#[error("error unpacking package")] #[error("error unpacking package")]
Unpack(#[from] std::io::Error), Unpack(#[from] std::io::Error),
} }
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum EntryKeyParseError {
#[error("malformed entry key {0}")]
Malformed(String),
#[error("malformed version")]
Version(#[from] semver::Error),
#[error("malformed target")]
Target(#[from] crate::manifest::errors::TargetKindFromStr),
}
} }

View file

@ -1,4 +1,4 @@
use crate::{names::PackageName, source::DependencySpecifier}; use crate::{manifest::TargetKind, names::PackageName, source::DependencySpecifier};
use semver::VersionReq; use semver::VersionReq;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Display; use std::fmt::Display;
@ -9,6 +9,8 @@ pub struct PesdeDependencySpecifier {
pub version: VersionReq, pub version: VersionReq,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub index: Option<String>, pub index: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target: Option<TargetKind>,
} }
impl DependencySpecifier for PesdeDependencySpecifier {} impl DependencySpecifier for PesdeDependencySpecifier {}