diff --git a/src/manifest.rs b/src/manifest.rs index 826924f..ab129e3 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -33,6 +33,22 @@ impl Display for TargetKind { } } +impl FromStr for TargetKind { + type Err = errors::TargetKindFromStr; + + fn from_str(s: &str) -> Result { + match s { + #[cfg(feature = "roblox")] + "roblox" => Ok(Self::Roblox), + #[cfg(feature = "lune")] + "lune" => Ok(Self::Lune), + #[cfg(feature = "luau")] + "luau" => Ok(Self::Luau), + t => Err(errors::TargetKindFromStr::Unknown(t.to_string())), + } + } +} + impl TargetKind { // self is the project's target, dependency is the target of the dependency pub fn is_compatible_with(&self, dependency: &Self) -> bool { @@ -304,4 +320,11 @@ pub mod errors { #[error("roblox target must have at least one build file")] NoBuildFiles, } + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum TargetKindFromStr { + #[error("unknown target kind {0}")] + Unknown(String), + } } diff --git a/src/source/pesde/mod.rs b/src/source/pesde/mod.rs index c2e21c8..491411d 100644 --- a/src/source/pesde/mod.rs +++ b/src/source/pesde/mod.rs @@ -1,15 +1,22 @@ -use std::{collections::BTreeMap, fmt::Debug, hash::Hash, path::Path}; +use std::{ + collections::BTreeMap, + fmt::{Debug, Display}, + hash::Hash, + path::Path, + str::FromStr, +}; use gix::remote::Direction; use semver::Version; use serde::{Deserialize, Serialize}; +use serde_with::{DeserializeFromStr, SerializeDisplay}; use pkg_ref::PesdePackageRef; use specifier::PesdeDependencySpecifier; use crate::{ git::authenticate_conn, - manifest::{DependencyType, Target}, + manifest::{DependencyType, Target, TargetKind}, names::{PackageName, PackageNames}, source::{hash, DependencySpecifiers, PackageSource, ResolveResult}, Project, REQWEST_CLIENT, @@ -315,8 +322,8 @@ impl PackageSource for PesdePackageSource { PackageNames::Pesde(specifier.name.clone()), entries .into_iter() - .filter(|(version, _)| specifier.version.matches(version)) - .map(|(version, entry)| { + .filter(|(EntryKey(version, _), _)| specifier.version.matches(version)) + .map(|(EntryKey(version, _), entry)| { ( version.clone(), PesdePackageRef { @@ -393,7 +400,7 @@ impl IndexConfig { } } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct IndexFileEntry { pub target: Target, #[serde(default = "chrono::Utc::now")] @@ -406,7 +413,33 @@ pub struct IndexFileEntry { pub dependencies: BTreeMap, } -pub type IndexFile = BTreeMap; +#[derive( + 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 { + 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; pub mod errors { use std::path::PathBuf; @@ -560,4 +593,17 @@ pub mod errors { #[error("error unpacking package")] 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), + } }