feat: implement patching

This commit is contained in:
daimond113 2024-07-23 16:37:47 +02:00
parent d4371519c2
commit 5661194721
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
14 changed files with 479 additions and 37 deletions

80
Cargo.lock generated
View file

@ -1081,6 +1081,21 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "git2"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
dependencies = [
"bitflags 2.6.0",
"libc",
"libgit2-sys",
"log",
"openssl-probe",
"openssl-sys",
"url",
]
[[package]] [[package]]
name = "gix" name = "gix"
version = "0.63.0" version = "0.63.0"
@ -2271,6 +2286,20 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "libgit2-sys"
version = "0.17.0+1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224"
dependencies = [
"cc",
"libc",
"libssh2-sys",
"libz-sys",
"openssl-sys",
"pkg-config",
]
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.3" version = "0.1.3"
@ -2281,6 +2310,32 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "libssh2-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "linux-keyutils" name = "linux-keyutils"
version = "0.2.4" version = "0.2.4"
@ -2553,6 +2608,24 @@ dependencies = [
"pathdiff", "pathdiff",
] ]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
@ -2638,6 +2711,7 @@ dependencies = [
"directories", "directories",
"flate2", "flate2",
"full_moon", "full_moon",
"git2",
"gix", "gix",
"indicatif", "indicatif",
"indicatif-log-bridge", "indicatif-log-bridge",
@ -3766,6 +3840,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"

View file

@ -30,6 +30,8 @@ toml = "0.8.15"
serde_json = "1.0.120" serde_json = "1.0.120"
serde_with = "3.9.0" serde_with = "3.9.0"
gix = { version = "0.63.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials"] } gix = { version = "0.63.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials"] }
# TODO: remove this when gitoxide adds support for: committing, pushing, adding
git2 = "0.19.0"
semver = { version = "1.0.23", features = ["serde"] } semver = { version = "1.0.23", features = ["serde"] }
reqwest = { version = "0.12.5", default-features = false, features = ["rustls-tls", "blocking"] } reqwest = { version = "0.12.5", default-features = false, features = ["rustls-tls", "blocking"] }
tar = "0.4.41" tar = "0.4.41"

View file

@ -68,19 +68,11 @@ impl InitCommand {
.prompt() .prompt()
.unwrap(); .unwrap();
let authors = authors authors
.split(',') .split(',')
.map(|s| s.trim()) .map(|s| s.trim())
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
.map(|s| s.into()) .for_each(|author| manifest["authors"].as_array_mut().unwrap().push(author));
.collect::<Vec<toml_edit::Value>>();
if !authors.is_empty() {
let mut authors_arr = toml_edit::Array::new();
authors_arr.extend(authors);
manifest["authors"] = toml_edit::value(authors_arr);
}
let repo = inquire::Text::new( let repo = inquire::Text::new(
"What is the repository URL of this project? (leave empty for none)", "What is the repository URL of this project? (leave empty for none)",
@ -124,8 +116,7 @@ impl InitCommand {
.prompt() .prompt()
.unwrap(); .unwrap();
let mut target = toml_edit::Table::new(); manifest["target"]["environment"] = toml_edit::value(target_env);
target["environment"] = toml_edit::value(target_env);
if target_env == "roblox" if target_env == "roblox"
|| inquire::Confirm::new(&format!( || inquire::Confirm::new(&format!(
@ -147,21 +138,14 @@ impl InitCommand {
) )
.context("failed to write script file")?; .context("failed to write script file")?;
let scripts = manifest manifest["scripts"][&ScriptName::RobloxSyncConfigGenerator.to_string()] =
.entry("scripts")
.or_insert(toml_edit::Item::Table(toml_edit::Table::new()))
.as_table_mut()
.unwrap();
scripts[&ScriptName::RobloxSyncConfigGenerator.to_string()] =
toml_edit::value(format!( toml_edit::value(format!(
concat!(".", env!("CARGO_PKG_NAME"), "/{}.luau"), concat!(".", env!("CARGO_PKG_NAME"), "/{}.luau"),
ScriptName::RobloxSyncConfigGenerator ScriptName::RobloxSyncConfigGenerator
)); ));
} }
let mut indices = toml_edit::Table::new(); manifest["indices"][DEFAULT_INDEX_NAME] =
indices[DEFAULT_INDEX_NAME] =
toml_edit::value(read_config(project.data_dir())?.default_index.as_str()); toml_edit::value(read_config(project.data_dir())?.default_index.as_str());
project.write_manifest(manifest.to_string())?; project.write_manifest(manifest.to_string())?;

View file

@ -97,6 +97,10 @@ impl InstallCommand {
.link_dependencies(&downloaded_graph) .link_dependencies(&downloaded_graph)
.context("failed to link dependencies")?; .context("failed to link dependencies")?;
project
.apply_patches(&downloaded_graph)
.context("failed to apply patches")?;
project project
.write_lockfile(Lockfile { .write_lockfile(Lockfile {
name: manifest.name, name: manifest.name,

View file

@ -3,14 +3,16 @@ use anyhow::Context;
use gix::remote::Direction; use gix::remote::Direction;
use indicatif::MultiProgress; use indicatif::MultiProgress;
use keyring::Entry; use keyring::Entry;
use pesde::Project; use pesde::{lockfile::DownloadedGraph, names::PackageNames, source::VersionId, Project};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashSet, path::Path}; use std::{collections::HashSet, path::Path, str::FromStr};
mod auth; mod auth;
mod config; mod config;
mod init; mod init;
mod install; mod install;
mod patch;
mod patch_commit;
mod publish; mod publish;
mod run; mod run;
mod self_install; mod self_install;
@ -280,6 +282,48 @@ impl IsUpToDate for Project {
} }
} }
#[derive(Debug, Clone)]
struct VersionedPackageName(PackageNames, Option<VersionId>);
impl FromStr for VersionedPackageName {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.splitn(2, '@');
let name = parts.next().unwrap();
let version = parts.next().map(VersionId::from_str).transpose()?;
Ok(VersionedPackageName(name.parse()?, version))
}
}
impl VersionedPackageName {
fn get(self, graph: &DownloadedGraph) -> anyhow::Result<(PackageNames, VersionId)> {
let version_id = match self.1 {
Some(version) => version,
None => {
let versions = graph.get(&self.0).context("package not found in graph")?;
if versions.len() == 1 {
let version = versions.keys().next().unwrap().clone();
log::debug!("only one version found, using {version}");
version
} else {
anyhow::bail!(
"multiple versions found, please specify one of: {}",
versions
.keys()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
}
};
Ok((self.0, version_id))
}
}
#[derive(Debug, clap::Subcommand)] #[derive(Debug, clap::Subcommand)]
pub enum Subcommand { pub enum Subcommand {
/// Authentication-related commands /// Authentication-related commands
@ -304,6 +348,12 @@ pub enum Subcommand {
/// Installs the pesde binary and scripts /// Installs the pesde binary and scripts
SelfInstall(self_install::SelfInstallCommand), SelfInstall(self_install::SelfInstallCommand),
/// Sets up a patching environment for a package
Patch(patch::PatchCommand),
/// Finalizes a patching environment for a package
PatchCommit(patch_commit::PatchCommitCommand),
} }
impl Subcommand { impl Subcommand {
@ -316,6 +366,8 @@ impl Subcommand {
Subcommand::Install(install) => install.run(project, multi), Subcommand::Install(install) => install.run(project, multi),
Subcommand::Publish(publish) => publish.run(project), Subcommand::Publish(publish) => publish.run(project),
Subcommand::SelfInstall(self_install) => self_install.run(project), Subcommand::SelfInstall(self_install) => self_install.run(project),
Subcommand::Patch(patch) => patch.run(project),
Subcommand::PatchCommit(patch_commit) => patch_commit.run(project),
} }
} }
} }

71
src/cli/patch.rs Normal file
View file

@ -0,0 +1,71 @@
use crate::cli::{reqwest_client, IsUpToDate, VersionedPackageName};
use anyhow::Context;
use clap::Args;
use colored::Colorize;
use pesde::{
patches::setup_patches_repo,
source::{PackageRef, PackageSource},
Project, MANIFEST_FILE_NAME,
};
#[derive(Debug, Args)]
pub struct PatchCommand {
/// The package name to patch
#[arg(index = 1)]
package: VersionedPackageName,
}
impl PatchCommand {
pub fn run(self, project: Project) -> anyhow::Result<()> {
let graph = if project.is_up_to_date(true)? {
project.deser_lockfile()?.graph
} else {
anyhow::bail!("outdated lockfile, please run the install command first")
};
let (name, version_id) = self.package.get(&graph)?;
let node = graph
.get(&name)
.and_then(|versions| versions.get(&version_id))
.context("package not found in graph")?;
let source = node.node.pkg_ref.source();
let directory = project
.data_dir()
.join("patches")
.join(name.escaped())
.join(version_id.escaped())
.join(chrono::Utc::now().timestamp().to_string());
std::fs::create_dir_all(&directory)?;
source.download(
&node.node.pkg_ref,
&directory,
&project,
&reqwest_client(project.data_dir())?,
)?;
// TODO: if MANIFEST_FILE_NAME does not exist, try to convert it
setup_patches_repo(&directory)?;
println!(
concat!(
"done! modify the files in the directory, then run `",
env!("CARGO_BIN_NAME"),
r#" patch-commit {}` to apply.
{}: do not commit these changes
{}: the {} file will be ignored when patching"#
),
directory.display().to_string().bold().cyan(),
"warning".yellow(),
"note".blue(),
MANIFEST_FILE_NAME
);
open::that(directory)?;
Ok(())
}
}

77
src/cli/patch_commit.rs Normal file
View file

@ -0,0 +1,77 @@
use crate::cli::IsUpToDate;
use anyhow::Context;
use clap::Args;
use pesde::{
manifest::Manifest, names::PackageNames, patches::create_patch, source::VersionId, Project,
MANIFEST_FILE_NAME,
};
use std::{path::PathBuf, str::FromStr};
#[derive(Debug, Args)]
pub struct PatchCommitCommand {
/// The directory containing the patch to commit
#[arg(index = 1)]
directory: PathBuf,
}
impl PatchCommitCommand {
pub fn run(self, project: Project) -> anyhow::Result<()> {
let graph = if project.is_up_to_date(true)? {
project.deser_lockfile()?.graph
} else {
anyhow::bail!("outdated lockfile, please run the install command first")
};
let (name, version_id) = {
let patched_manifest = std::fs::read_to_string(self.directory.join(MANIFEST_FILE_NAME))
.context("failed to read patched manifest")?;
let patched_manifest: Manifest =
toml::from_str(&patched_manifest).context("failed to parse patched manifest")?;
(
PackageNames::Pesde(patched_manifest.name),
VersionId::new(patched_manifest.version, patched_manifest.target.kind()),
)
};
graph
.get(&name)
.and_then(|versions| versions.get(&version_id))
.context("package not found in graph")?;
let mut manifest = toml_edit::DocumentMut::from_str(
&project.read_manifest().context("failed to read manifest")?,
)
.context("failed to parse manifest")?;
let patch = create_patch(&self.directory).context("failed to create patch")?;
std::fs::remove_dir_all(self.directory).context("failed to remove patch directory")?;
let patches_dir = project.path().join("patches");
std::fs::create_dir_all(&patches_dir).context("failed to create patches directory")?;
let patch_file_name = format!("{}-{}.patch", name.escaped(), version_id.escaped(),);
let patch_file = patches_dir.join(&patch_file_name);
if patch_file.exists() {
anyhow::bail!("patch file already exists: {}", patch_file.display());
}
std::fs::write(&patch_file, patch).context("failed to write patch file")?;
manifest["patches"][&name.to_string()][&version_id.to_string()] =
toml_edit::value(format!("patches/{patch_file_name}"));
project
.write_manifest(manifest.to_string())
.context("failed to write manifest")?;
println!(concat!(
"done! run `",
env!("CARGO_BIN_NAME"),
" install` to apply the patch"
));
Ok(())
}
}

View file

@ -6,7 +6,7 @@ use std::{
use crate::{ use crate::{
lockfile::{DependencyGraph, DownloadedDependencyGraphNode, DownloadedGraph}, lockfile::{DependencyGraph, DownloadedDependencyGraphNode, DownloadedGraph},
source::{pesde::PesdePackageSource, PackageRefs, PackageSource, PackageSources}, source::{PackageRef, PackageSource, PackageSources},
Project, PACKAGES_CONTAINER_NAME, Project, PACKAGES_CONTAINER_NAME,
}; };
@ -33,11 +33,7 @@ impl Project {
for (name, versions) in graph { for (name, versions) in graph {
for (version_id, node) in versions { for (version_id, node) in versions {
let source = match &node.pkg_ref { let source = node.pkg_ref.source();
PackageRefs::Pesde(pkg_ref) => {
PackageSources::Pesde(PesdePackageSource::new(pkg_ref.index_url.clone()))
}
};
if refreshed_sources.insert(source.clone()) { if refreshed_sources.insert(source.clone()) {
source.refresh(self).map_err(Box::new)?; source.refresh(self).map_err(Box::new)?;

View file

@ -11,6 +11,7 @@ pub mod linking;
pub mod lockfile; pub mod lockfile;
pub mod manifest; pub mod manifest;
pub mod names; pub mod names;
pub mod patches;
pub mod resolver; pub mod resolver;
pub mod scripts; pub mod scripts;
pub mod source; pub mod source;
@ -87,9 +88,9 @@ impl Project {
&self.auth_config &self.auth_config
} }
pub fn read_manifest(&self) -> Result<Vec<u8>, errors::ManifestReadError> { pub fn read_manifest(&self) -> Result<String, errors::ManifestReadError> {
let bytes = std::fs::read(self.path.join(MANIFEST_FILE_NAME))?; let string = std::fs::read_to_string(self.path.join(MANIFEST_FILE_NAME))?;
Ok(bytes) Ok(string)
} }
pub fn deser_manifest(&self) -> Result<manifest::Manifest, errors::ManifestReadError> { pub fn deser_manifest(&self) -> Result<manifest::Manifest, errors::ManifestReadError> {

View file

@ -1,4 +1,7 @@
use crate::{names::PackageName, source::DependencySpecifiers}; use crate::{
names::{PackageName, PackageNames},
source::{DependencySpecifiers, VersionId},
};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use semver::Version; use semver::Version;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -251,6 +254,8 @@ pub struct Manifest {
pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>, pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>,
#[serde(default)] #[serde(default)]
pub includes: BTreeSet<String>, pub includes: BTreeSet<String>,
#[serde(default, skip_serializing)]
pub patches: BTreeMap<PackageNames, BTreeMap<VersionId, RelativePathBuf>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: BTreeMap<String, DependencySpecifiers>, pub dependencies: BTreeMap<String, DependencySpecifiers>,

View file

@ -1,6 +1,5 @@
use std::{fmt::Display, str::FromStr}; use std::{fmt::Display, str::FromStr};
use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay}; use serde_with::{DeserializeFromStr, SerializeDisplay};
#[derive(Debug)] #[derive(Debug)]
@ -69,8 +68,9 @@ impl PackageName {
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(
#[serde(untagged)] Debug, DeserializeFromStr, SerializeDisplay, Clone, Hash, PartialEq, Eq, PartialOrd, Ord,
)]
pub enum PackageNames { pub enum PackageNames {
Pesde(PackageName), Pesde(PackageName),
} }
@ -97,6 +97,18 @@ impl Display for PackageNames {
} }
} }
impl FromStr for PackageNames {
type Err = errors::PackageNamesError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(name) = PackageName::from_str(s) {
Ok(PackageNames::Pesde(name))
} else {
Err(errors::PackageNamesError::InvalidPackageName(s.to_string()))
}
}
}
pub mod errors { pub mod errors {
use thiserror::Error; use thiserror::Error;
@ -119,4 +131,11 @@ pub mod errors {
#[error("package {0} `{1}` is not within 3-32 characters long")] #[error("package {0} `{1}` is not within 3-32 characters long")]
InvalidLength(ErrorReason, String), InvalidLength(ErrorReason, String),
} }
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PackageNamesError {
#[error("invalid package name {0}")]
InvalidPackageName(String),
}
} }

132
src/patches.rs Normal file
View file

@ -0,0 +1,132 @@
use crate::{lockfile::DownloadedGraph, Project, MANIFEST_FILE_NAME, PACKAGES_CONTAINER_NAME};
use git2::{ApplyLocation, Diff, DiffFormat, DiffLineType, Repository, Signature};
use std::{fs::read, path::Path};
pub fn setup_patches_repo<P: AsRef<Path>>(dir: P) -> Result<Repository, git2::Error> {
let repo = Repository::init(&dir)?;
{
let signature = Signature::now(
env!("CARGO_PKG_NAME"),
concat!(env!("CARGO_PKG_NAME"), "@localhost"),
)?;
let mut index = repo.index()?;
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
index.write()?;
let oid = index.write_tree()?;
let tree = repo.find_tree(oid)?;
repo.commit(
Some("HEAD"),
&signature,
&signature,
"begin patch",
&tree,
&[],
)?;
}
Ok(repo)
}
pub fn create_patch<P: AsRef<Path>>(dir: P) -> Result<Vec<u8>, git2::Error> {
let mut patches = vec![];
let repo = Repository::open(dir.as_ref())?;
let original = repo.head()?.peel_to_tree()?;
// reset the manifest file to the original state
let mut checkout_builder = git2::build::CheckoutBuilder::new();
checkout_builder.force();
checkout_builder.path(MANIFEST_FILE_NAME);
repo.checkout_tree(original.as_object(), Some(&mut checkout_builder))?;
let diff = repo.diff_tree_to_workdir(Some(&original), None)?;
diff.print(DiffFormat::Patch, |_delta, _hunk, line| {
if matches!(
line.origin_value(),
DiffLineType::Context | DiffLineType::Addition | DiffLineType::Deletion
) {
let origin = line.origin();
let mut buffer = vec![0; origin.len_utf8()];
origin.encode_utf8(&mut buffer);
patches.extend(buffer);
}
patches.extend(line.content());
true
})?;
Ok(patches)
}
impl Project {
pub fn apply_patches(&self, graph: &DownloadedGraph) -> Result<(), errors::ApplyPatchesError> {
let manifest = self.deser_manifest()?;
for (name, versions) in manifest.patches {
for (version_id, patch_path) in versions {
let patch_path = patch_path.to_path(self.path());
let patch = Diff::from_buffer(&read(&patch_path).map_err(|e| {
errors::ApplyPatchesError::PatchReadError(patch_path.clone(), e)
})?)?;
let Some(node) = graph
.get(&name)
.and_then(|versions| versions.get(&version_id))
else {
return Err(errors::ApplyPatchesError::PackageNotFound(name, version_id));
};
let container_folder = node.node.container_folder(
&self
.path()
.join(node.node.base_folder(manifest.target.kind(), true))
.join(PACKAGES_CONTAINER_NAME),
&name,
version_id.version(),
);
{
let repo = setup_patches_repo(&container_folder)?;
repo.apply(&patch, ApplyLocation::Both, None)?;
}
std::fs::remove_dir_all(container_folder.join(".git")).map_err(|e| {
errors::ApplyPatchesError::GitDirectoryRemovalError(container_folder, e)
})?;
}
}
Ok(())
}
}
pub mod errors {
use std::path::PathBuf;
use crate::{names::PackageNames, source::VersionId};
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ApplyPatchesError {
#[error("error deserializing project manifest")]
ManifestDeserializationFailed(#[from] crate::errors::ManifestReadError),
#[error("error interacting with git")]
GitError(#[from] git2::Error),
#[error("error reading patch file at {0}")]
PatchReadError(PathBuf, #[source] std::io::Error),
#[error("error removing .git directory")]
GitDirectoryRemovalError(PathBuf, #[source] std::io::Error),
#[error("package {0}@{1} not found in graph")]
PackageNotFound(PackageNames, VersionId),
}
}

View file

@ -48,6 +48,7 @@ pub trait PackageRef: Debug {
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)>; fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)>;
fn use_new_structure(&self) -> bool; fn use_new_structure(&self) -> bool;
fn target_kind(&self) -> TargetKind; fn target_kind(&self) -> TargetKind;
fn source(&self) -> PackageSources;
} }
impl PackageRef for PackageRefs { impl PackageRef for PackageRefs {
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)> { fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)> {
@ -67,6 +68,12 @@ impl PackageRef for PackageRefs {
PackageRefs::Pesde(pkg_ref) => pkg_ref.target_kind(), PackageRefs::Pesde(pkg_ref) => pkg_ref.target_kind(),
} }
} }
fn source(&self) -> PackageSources {
match self {
PackageRefs::Pesde(pkg_ref) => pkg_ref.source(),
}
}
} }
#[derive( #[derive(
@ -75,6 +82,10 @@ impl PackageRef for PackageRefs {
pub struct VersionId(Version, TargetKind); pub struct VersionId(Version, TargetKind);
impl VersionId { impl VersionId {
pub fn new(version: Version, target: TargetKind) -> Self {
VersionId(version, target)
}
pub fn version(&self) -> &Version { pub fn version(&self) -> &Version {
&self.0 &self.0
} }
@ -82,6 +93,10 @@ impl VersionId {
pub fn target(&self) -> &TargetKind { pub fn target(&self) -> &TargetKind {
&self.1 &self.1
} }
pub fn escaped(&self) -> String {
format!("{}+{}", self.0, self.1)
}
} }
impl Display for VersionId { impl Display for VersionId {

View file

@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
manifest::{DependencyType, Target, TargetKind}, manifest::{DependencyType, Target, TargetKind},
names::PackageName, names::PackageName,
source::{DependencySpecifiers, PackageRef}, source::{pesde::PesdePackageSource, DependencySpecifiers, PackageRef, PackageSources},
}; };
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
@ -34,6 +34,10 @@ impl PackageRef for PesdePackageRef {
fn target_kind(&self) -> TargetKind { fn target_kind(&self) -> TargetKind {
self.target.kind() self.target.kind()
} }
fn source(&self) -> PackageSources {
PackageSources::Pesde(PesdePackageSource::new(self.index_url.clone()))
}
} }
impl Ord for PesdePackageRef { impl Ord for PesdePackageRef {