mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-12 11:00:36 +00:00
feat: implement workspace/monorepo support
This commit is contained in:
parent
bd7e1452b0
commit
f1ce6283d8
34 changed files with 880 additions and 202 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -2335,6 +2335,12 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "governor"
|
name = "governor"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
@ -2855,9 +2861,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyring"
|
name = "keyring"
|
||||||
version = "3.2.0"
|
version = "3.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73b9af47ded4df3067484d7d45758ca2b36bd083bf6d024c2952bbd8af1cdaa4"
|
checksum = "030a9b84bb2a2f3673d4c8b8236091ed5d8f6b66a56d8085471d8abd5f3c6a80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"dbus-secret-service",
|
"dbus-secret-service",
|
||||||
|
@ -3506,6 +3512,7 @@ dependencies = [
|
||||||
"full_moon",
|
"full_moon",
|
||||||
"git2",
|
"git2",
|
||||||
"gix",
|
"gix",
|
||||||
|
"glob",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"indicatif-log-bridge",
|
"indicatif-log-bridge",
|
||||||
"inquire",
|
"inquire",
|
||||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -43,14 +43,14 @@ required-features = ["bin"]
|
||||||
uninlined_format_args = "warn"
|
uninlined_format_args = "warn"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.209", features = ["derive"] }
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
serde_with = "3.9.0"
|
serde_with = "3.9.0"
|
||||||
gix = { version = "0.66.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials"] }
|
gix = { version = "0.66.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials"] }
|
||||||
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.7", default-features = false, features = ["rustls-tls", "blocking"] }
|
||||||
tar = "0.4.41"
|
tar = "0.4.41"
|
||||||
flate2 = "1.0.31"
|
flate2 = "1.0.33"
|
||||||
pathdiff = "0.2.1"
|
pathdiff = "0.2.1"
|
||||||
relative-path = { version = "1.9.3", features = ["serde"] }
|
relative-path = { version = "1.9.3", features = ["serde"] }
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
|
@ -63,23 +63,24 @@ url = { version = "2.5.2", features = ["serde"] }
|
||||||
chrono = { version = "0.4.38", features = ["serde"] }
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
tempfile = "3.12.0"
|
tempfile = "3.12.0"
|
||||||
|
glob = "0.3.1"
|
||||||
|
|
||||||
# TODO: remove this when gitoxide adds support for: committing, pushing, adding
|
# TODO: remove this when gitoxide adds support for: committing, pushing, adding
|
||||||
git2 = { version = "0.19.0", optional = true }
|
git2 = { version = "0.19.0", optional = true }
|
||||||
|
|
||||||
zip = { version = "2.1.6", optional = true }
|
zip = { version = "2.2.0", optional = true }
|
||||||
serde_json = { version = "1.0.122", optional = true }
|
serde_json = { version = "1.0.127", optional = true }
|
||||||
|
|
||||||
anyhow = { version = "1.0.86", optional = true }
|
anyhow = { version = "1.0.86", optional = true }
|
||||||
open = { version = "5.3.0", optional = true }
|
open = { version = "5.3.0", optional = true }
|
||||||
keyring = { version = "3.0.5", features = ["crypto-rust", "windows-native", "apple-native", "sync-secret-service"], optional = true }
|
keyring = { version = "3.2.1", features = ["crypto-rust", "windows-native", "apple-native", "sync-secret-service"], optional = true }
|
||||||
colored = { version = "2.1.0", optional = true }
|
colored = { version = "2.1.0", optional = true }
|
||||||
toml_edit = { version = "0.22.20", optional = true }
|
toml_edit = { version = "0.22.20", optional = true }
|
||||||
clap = { version = "4.5.13", features = ["derive"], optional = true }
|
clap = { version = "4.5.16", features = ["derive"], optional = true }
|
||||||
dirs = { version = "5.0.1", optional = true }
|
dirs = { version = "5.0.1", optional = true }
|
||||||
pretty_env_logger = { version = "0.5.0", optional = true }
|
pretty_env_logger = { version = "0.5.0", optional = true }
|
||||||
indicatif = { version = "0.17.8", optional = true }
|
indicatif = { version = "0.17.8", optional = true }
|
||||||
indicatif-log-bridge = { version = "0.2.2", optional = true }
|
indicatif-log-bridge = { version = "0.2.3", optional = true }
|
||||||
inquire = { version = "0.7.5", optional = true }
|
inquire = { version = "0.7.5", optional = true }
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
|
|
@ -298,13 +298,6 @@ pub async fn publish_package(
|
||||||
{
|
{
|
||||||
return Err(Error::InvalidArchive);
|
return Err(Error::InvalidArchive);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (dep_scope, dep_name) = specifier.name.as_str();
|
|
||||||
match source.read_file([dep_scope, dep_name], &app_state.project, None) {
|
|
||||||
Ok(Some(_)) => {}
|
|
||||||
Ok(None) => return Err(Error::InvalidArchive),
|
|
||||||
Err(e) => return Err(e.into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DependencySpecifiers::Wally(specifier) => {
|
DependencySpecifiers::Wally(specifier) => {
|
||||||
if !config.wally_allowed {
|
if !config.wally_allowed {
|
||||||
|
@ -325,6 +318,10 @@ pub async fn publish_package(
|
||||||
return Err(Error::InvalidArchive);
|
return Err(Error::InvalidArchive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DependencySpecifiers::Workspace(_) => {
|
||||||
|
// workspace specifiers are to be transformed into Pesde specifiers by the sender
|
||||||
|
return Err(Error::InvalidArchive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::{env::current_dir, fs::create_dir_all, sync::Mutex};
|
|
||||||
|
|
||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
use actix_governor::{Governor, GovernorConfigBuilder};
|
use actix_governor::{Governor, GovernorConfigBuilder};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
|
@ -9,6 +7,7 @@ use actix_web::{
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use rusty_s3::{Bucket, Credentials, UrlStyle};
|
use rusty_s3::{Bucket, Credentials, UrlStyle};
|
||||||
|
use std::{env::current_dir, fs::create_dir_all, path::PathBuf, sync::Mutex};
|
||||||
|
|
||||||
use pesde::{
|
use pesde::{
|
||||||
source::{pesde::PesdePackageSource, traits::PackageSource},
|
source::{pesde::PesdePackageSource, traits::PackageSource},
|
||||||
|
@ -78,6 +77,7 @@ async fn run(with_sentry: bool) -> std::io::Result<()> {
|
||||||
|
|
||||||
let project = Project::new(
|
let project = Project::new(
|
||||||
&cwd,
|
&cwd,
|
||||||
|
None::<PathBuf>,
|
||||||
data_dir.join("project"),
|
data_dir.join("project"),
|
||||||
&cwd,
|
&cwd,
|
||||||
AuthConfig::new().with_git_credentials(Some(gix::sec::identity::Account {
|
AuthConfig::new().with_git_credentials(Some(gix::sec::identity::Account {
|
||||||
|
|
|
@ -197,6 +197,7 @@ impl AddCommand {
|
||||||
|
|
||||||
println!("added git {}#{} to {}", spec.repo, spec.rev, dependency_key);
|
println!("added git {}#{} to {}", spec.repo, spec.rev, dependency_key);
|
||||||
}
|
}
|
||||||
|
DependencySpecifiers::Workspace(_) => todo!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
project
|
project
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl ExecuteCommand {
|
||||||
.arg(bin_path.to_path(tempdir.path()))
|
.arg(bin_path.to_path(tempdir.path()))
|
||||||
.arg("--")
|
.arg("--")
|
||||||
.args(&self.args)
|
.args(&self.args)
|
||||||
.current_dir(project.path())
|
.current_dir(project.package_dir())
|
||||||
.status()
|
.status()
|
||||||
.context("failed to run script")?;
|
.context("failed to run script")?;
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,9 @@ impl InitCommand {
|
||||||
.prompt()
|
.prompt()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
{
|
{
|
||||||
let folder = project.path().join(concat!(".", env!("CARGO_PKG_NAME")));
|
let folder = project
|
||||||
|
.package_dir()
|
||||||
|
.join(concat!(".", env!("CARGO_PKG_NAME")));
|
||||||
std::fs::create_dir_all(&folder).context("failed to create scripts folder")?;
|
std::fs::create_dir_all(&folder).context("failed to create scripts folder")?;
|
||||||
|
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
|
use crate::cli::{bin_dir, files::make_executable, IsUpToDate};
|
||||||
|
use anyhow::Context;
|
||||||
|
use clap::Args;
|
||||||
|
use indicatif::MultiProgress;
|
||||||
|
use pesde::{lockfile::Lockfile, manifest::target::TargetKind, Project, MANIFEST_FILE_NAME};
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeSet, HashSet},
|
collections::{BTreeSet, HashSet},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use clap::Args;
|
|
||||||
use indicatif::MultiProgress;
|
|
||||||
|
|
||||||
use pesde::{lockfile::Lockfile, manifest::target::TargetKind, Project, MANIFEST_FILE_NAME};
|
|
||||||
|
|
||||||
use crate::cli::{bin_dir, files::make_executable, IsUpToDate};
|
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
pub struct InstallCommand {
|
pub struct InstallCommand {
|
||||||
/// The amount of threads to use for downloading
|
/// The amount of threads to use for downloading
|
||||||
|
@ -44,7 +42,7 @@ fn bin_link_file(alias: &str) -> String {
|
||||||
let prefix = String::new();
|
let prefix = String::new();
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let prefix = "#!/usr/bin/env -S lune run\n";
|
let prefix = "#!/usr/bin/env -S lune run\n";
|
||||||
|
// TODO: reimplement workspace support in this
|
||||||
format!(
|
format!(
|
||||||
r#"{prefix}local process = require("@lune/process")
|
r#"{prefix}local process = require("@lune/process")
|
||||||
local fs = require("@lune/fs")
|
local fs = require("@lune/fs")
|
||||||
|
@ -113,7 +111,7 @@ impl InstallCommand {
|
||||||
if deleted_folders.insert(folder.to_string()) {
|
if deleted_folders.insert(folder.to_string()) {
|
||||||
log::debug!("deleting the {folder} folder");
|
log::debug!("deleting the {folder} folder");
|
||||||
|
|
||||||
if let Some(e) = std::fs::remove_dir_all(project.path().join(&folder))
|
if let Some(e) = std::fs::remove_dir_all(project.package_dir().join(&folder))
|
||||||
.err()
|
.err()
|
||||||
.filter(|e| e.kind() != std::io::ErrorKind::NotFound)
|
.filter(|e| e.kind() != std::io::ErrorKind::NotFound)
|
||||||
{
|
{
|
||||||
|
@ -150,7 +148,7 @@ impl InstallCommand {
|
||||||
"{msg} {bar:40.208/166} {pos}/{len} {percent}% {elapsed_precise}",
|
"{msg} {bar:40.208/166} {pos}/{len} {percent}% {elapsed_precise}",
|
||||||
)?,
|
)?,
|
||||||
)
|
)
|
||||||
.with_message("downloading dependencies"),
|
.with_message(format!("downloading dependencies of {}", manifest.name)),
|
||||||
);
|
);
|
||||||
bar.enable_steady_tick(Duration::from_millis(100));
|
bar.enable_steady_tick(Duration::from_millis(100));
|
||||||
|
|
||||||
|
@ -172,7 +170,10 @@ impl InstallCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bar.finish_with_message("finished downloading dependencies");
|
bar.finish_with_message(format!(
|
||||||
|
"finished downloading dependencies of {}",
|
||||||
|
manifest.name
|
||||||
|
));
|
||||||
|
|
||||||
let downloaded_graph = Arc::into_inner(downloaded_graph)
|
let downloaded_graph = Arc::into_inner(downloaded_graph)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -229,6 +230,46 @@ impl InstallCommand {
|
||||||
version: manifest.version,
|
version: manifest.version,
|
||||||
target: manifest.target.kind(),
|
target: manifest.target.kind(),
|
||||||
overrides: manifest.overrides,
|
overrides: manifest.overrides,
|
||||||
|
workspace: match project.workspace_dir() {
|
||||||
|
Some(_) => {
|
||||||
|
// this might seem counterintuitive, but remember that the workspace
|
||||||
|
// is the package_dir when the user isn't in a member package
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
None => project
|
||||||
|
.workspace_members(project.package_dir())
|
||||||
|
.context("failed to get workspace members")?
|
||||||
|
.into_iter()
|
||||||
|
.map(|(path, manifest)| {
|
||||||
|
(
|
||||||
|
manifest.name,
|
||||||
|
RelativePathBuf::from_path(
|
||||||
|
path.strip_prefix(project.package_dir()).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|(name, path)| {
|
||||||
|
InstallCommand {
|
||||||
|
threads: self.threads,
|
||||||
|
unlocked: self.unlocked,
|
||||||
|
}
|
||||||
|
.run(
|
||||||
|
Project::new(
|
||||||
|
path.to_path(project.package_dir()),
|
||||||
|
Some(project.package_dir()),
|
||||||
|
project.data_dir(),
|
||||||
|
project.cas_dir(),
|
||||||
|
project.auth_config().clone(),
|
||||||
|
),
|
||||||
|
multi.clone(),
|
||||||
|
reqwest.clone(),
|
||||||
|
)
|
||||||
|
.map(|_| (name, path))
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.context("failed to install workspace member's dependencies")?,
|
||||||
|
},
|
||||||
|
|
||||||
graph: downloaded_graph,
|
graph: downloaded_graph,
|
||||||
})
|
})
|
||||||
|
|
|
@ -35,7 +35,10 @@ impl OutdatedCommand {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(specifier, DependencySpecifiers::Git(_)) {
|
if matches!(
|
||||||
|
specifier,
|
||||||
|
DependencySpecifiers::Git(_) | DependencySpecifiers::Workspace(_)
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ impl OutdatedCommand {
|
||||||
spec.version = VersionReq::STAR;
|
spec.version = VersionReq::STAR;
|
||||||
}
|
}
|
||||||
DependencySpecifiers::Git(_) => {}
|
DependencySpecifiers::Git(_) => {}
|
||||||
|
DependencySpecifiers::Workspace(_) => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl PatchCommitCommand {
|
||||||
let patch = create_patch(&self.directory).context("failed to create patch")?;
|
let patch = create_patch(&self.directory).context("failed to create patch")?;
|
||||||
std::fs::remove_dir_all(self.directory).context("failed to remove patch directory")?;
|
std::fs::remove_dir_all(self.directory).context("failed to remove patch directory")?;
|
||||||
|
|
||||||
let patches_dir = project.path().join("patches");
|
let patches_dir = project.package_dir().join("patches");
|
||||||
std::fs::create_dir_all(&patches_dir).context("failed to create patches directory")?;
|
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_name = format!("{}-{}.patch", name.escaped(), version_id.escaped(),);
|
||||||
|
|
|
@ -7,13 +7,17 @@ use anyhow::Context;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
|
use semver::VersionReq;
|
||||||
use tempfile::tempfile;
|
use tempfile::tempfile;
|
||||||
|
|
||||||
use pesde::{
|
use pesde::{
|
||||||
manifest::target::Target,
|
manifest::target::Target,
|
||||||
scripts::ScriptName,
|
scripts::ScriptName,
|
||||||
source::{
|
source::{
|
||||||
pesde::PesdePackageSource, specifiers::DependencySpecifiers, traits::PackageSource,
|
pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource},
|
||||||
|
specifiers::DependencySpecifiers,
|
||||||
|
traits::PackageSource,
|
||||||
|
workspace::{specifier::VersionType, WorkspacePackageSource},
|
||||||
IGNORED_DIRS, IGNORED_FILES,
|
IGNORED_DIRS, IGNORED_FILES,
|
||||||
},
|
},
|
||||||
Project, DEFAULT_INDEX_NAME, MANIFEST_FILE_NAME,
|
Project, DEFAULT_INDEX_NAME, MANIFEST_FILE_NAME,
|
||||||
|
@ -52,9 +56,10 @@ impl PublishCommand {
|
||||||
#[cfg(feature = "roblox")]
|
#[cfg(feature = "roblox")]
|
||||||
let mut display_build_files: Vec<String> = vec![];
|
let mut display_build_files: Vec<String> = vec![];
|
||||||
|
|
||||||
let (lib_path, bin_path) = (
|
let (lib_path, bin_path, target_kind) = (
|
||||||
manifest.target.lib_path().cloned(),
|
manifest.target.lib_path().cloned(),
|
||||||
manifest.target.bin_path().cloned(),
|
manifest.target.bin_path().cloned(),
|
||||||
|
manifest.target.kind(),
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "roblox")]
|
#[cfg(feature = "roblox")]
|
||||||
|
@ -124,7 +129,7 @@ impl PublishCommand {
|
||||||
for (name, path) in [("lib path", lib_path), ("bin path", bin_path)] {
|
for (name, path) in [("lib path", lib_path), ("bin path", bin_path)] {
|
||||||
let Some(export_path) = path else { continue };
|
let Some(export_path) = path else { continue };
|
||||||
|
|
||||||
let export_path = export_path.to_path(project.path());
|
let export_path = export_path.to_path(project.package_dir());
|
||||||
if !export_path.exists() {
|
if !export_path.exists() {
|
||||||
anyhow::bail!("{name} points to non-existent file");
|
anyhow::bail!("{name} points to non-existent file");
|
||||||
}
|
}
|
||||||
|
@ -146,7 +151,7 @@ impl PublishCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
let first_part = export_path
|
let first_part = export_path
|
||||||
.strip_prefix(project.path())
|
.strip_prefix(project.package_dir())
|
||||||
.context(format!("{name} not within project directory"))?
|
.context(format!("{name} not within project directory"))?
|
||||||
.components()
|
.components()
|
||||||
.next()
|
.next()
|
||||||
|
@ -177,7 +182,7 @@ impl PublishCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
for included_name in &manifest.includes {
|
for included_name in &manifest.includes {
|
||||||
let included_path = project.path().join(included_name);
|
let included_path = project.package_dir().join(included_name);
|
||||||
|
|
||||||
if !included_path.exists() {
|
if !included_path.exists() {
|
||||||
anyhow::bail!("included file {included_name} does not exist");
|
anyhow::bail!("included file {included_name} does not exist");
|
||||||
|
@ -216,7 +221,7 @@ impl PublishCommand {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let build_file_path = project.path().join(build_file);
|
let build_file_path = project.package_dir().join(build_file);
|
||||||
|
|
||||||
if !build_file_path.exists() {
|
if !build_file_path.exists() {
|
||||||
anyhow::bail!("build file {build_file} does not exist");
|
anyhow::bail!("build file {build_file} does not exist");
|
||||||
|
@ -281,6 +286,45 @@ impl PublishCommand {
|
||||||
DependencySpecifiers::Git(_) => {
|
DependencySpecifiers::Git(_) => {
|
||||||
has_git = true;
|
has_git = true;
|
||||||
}
|
}
|
||||||
|
DependencySpecifiers::Workspace(spec) => {
|
||||||
|
let pkg_ref = WorkspacePackageSource
|
||||||
|
.resolve(spec, &project, target_kind)
|
||||||
|
.context("failed to resolve workspace package")?
|
||||||
|
.1
|
||||||
|
.pop_last()
|
||||||
|
.context("no versions found for workspace package")?
|
||||||
|
.1;
|
||||||
|
|
||||||
|
let manifest = pkg_ref
|
||||||
|
.path
|
||||||
|
.to_path(
|
||||||
|
project
|
||||||
|
.workspace_dir()
|
||||||
|
.context("failed to get workspace directory")?,
|
||||||
|
)
|
||||||
|
.join(MANIFEST_FILE_NAME);
|
||||||
|
let manifest = std::fs::read_to_string(&manifest)
|
||||||
|
.context("failed to read workspace package manifest")?;
|
||||||
|
let manifest = toml::from_str::<pesde::manifest::Manifest>(&manifest)
|
||||||
|
.context("failed to parse workspace package manifest")?;
|
||||||
|
|
||||||
|
*specifier = DependencySpecifiers::Pesde(PesdeDependencySpecifier {
|
||||||
|
name: spec.name.clone(),
|
||||||
|
version: match spec.version_type {
|
||||||
|
VersionType::Wildcard => VersionReq::STAR,
|
||||||
|
v => VersionReq::parse(&format!("{v}{}", manifest.version))
|
||||||
|
.context(format!("failed to parse version for {v}"))?,
|
||||||
|
},
|
||||||
|
index: Some(
|
||||||
|
manifest
|
||||||
|
.indices
|
||||||
|
.get(DEFAULT_INDEX_NAME)
|
||||||
|
.context("missing default index in workspace package manifest")?
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
target: Some(manifest.target.kind()),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,18 +4,18 @@ use anyhow::Context;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
|
use crate::cli::IsUpToDate;
|
||||||
use pesde::{
|
use pesde::{
|
||||||
names::{PackageName, PackageNames},
|
names::{PackageName, PackageNames},
|
||||||
|
source::traits::PackageRef,
|
||||||
Project, PACKAGES_CONTAINER_NAME,
|
Project, PACKAGES_CONTAINER_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::cli::IsUpToDate;
|
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
pub struct RunCommand {
|
pub struct RunCommand {
|
||||||
/// The package name, script name, or path to a script to run
|
/// The package name, script name, or path to a script to run
|
||||||
#[arg(index = 1)]
|
#[arg(index = 1)]
|
||||||
package_or_script: String,
|
package_or_script: Option<String>,
|
||||||
|
|
||||||
/// Arguments to pass to the script
|
/// Arguments to pass to the script
|
||||||
#[arg(index = 2, last = true)]
|
#[arg(index = 2, last = true)]
|
||||||
|
@ -30,14 +30,25 @@ impl RunCommand {
|
||||||
.arg(path)
|
.arg(path)
|
||||||
.arg("--")
|
.arg("--")
|
||||||
.args(&self.args)
|
.args(&self.args)
|
||||||
.current_dir(project.path())
|
.current_dir(project.package_dir())
|
||||||
.status()
|
.status()
|
||||||
.expect("failed to run script");
|
.expect("failed to run script");
|
||||||
|
|
||||||
std::process::exit(status.code().unwrap_or(1))
|
std::process::exit(status.code().unwrap_or(1))
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(pkg_name) = self.package_or_script.parse::<PackageName>() {
|
let package_or_script = match self.package_or_script {
|
||||||
|
Some(package_or_script) => package_or_script,
|
||||||
|
None => {
|
||||||
|
if let Some(script_path) = project.deser_manifest()?.target.bin_path() {
|
||||||
|
run(script_path.to_path(project.package_dir()));
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::bail!("no package or script specified")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(pkg_name) = package_or_script.parse::<PackageName>() {
|
||||||
let graph = if project.is_up_to_date(true)? {
|
let graph = if project.is_up_to_date(true)? {
|
||||||
project.deser_lockfile()?.graph
|
project.deser_lockfile()?.graph
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,12 +66,14 @@ impl RunCommand {
|
||||||
anyhow::bail!("package has no bin path");
|
anyhow::bail!("package has no bin path");
|
||||||
};
|
};
|
||||||
|
|
||||||
let base_folder = node
|
let base_folder = project
|
||||||
.node
|
.deser_manifest()?
|
||||||
.base_folder(project.deser_manifest()?.target.kind(), true);
|
.target
|
||||||
|
.kind()
|
||||||
|
.packages_folder(&node.node.pkg_ref.target_kind());
|
||||||
let container_folder = node.node.container_folder(
|
let container_folder = node.node.container_folder(
|
||||||
&project
|
&project
|
||||||
.path()
|
.package_dir()
|
||||||
.join(base_folder)
|
.join(base_folder)
|
||||||
.join(PACKAGES_CONTAINER_NAME),
|
.join(PACKAGES_CONTAINER_NAME),
|
||||||
&pkg_name,
|
&pkg_name,
|
||||||
|
@ -72,13 +85,13 @@ impl RunCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(manifest) = project.deser_manifest() {
|
if let Ok(manifest) = project.deser_manifest() {
|
||||||
if let Some(script_path) = manifest.scripts.get(&self.package_or_script) {
|
if let Some(script_path) = manifest.scripts.get(&package_or_script) {
|
||||||
run(script_path.to_path(project.path()))
|
run(script_path.to_path(project.package_dir()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let relative_path = RelativePathBuf::from(self.package_or_script);
|
let relative_path = RelativePathBuf::from(package_or_script);
|
||||||
let path = relative_path.to_path(project.path());
|
let path = relative_path.to_path(project.package_dir());
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
anyhow::bail!("path does not exist: {}", path.display());
|
anyhow::bail!("path does not exist: {}", path.display());
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn make_executable<P: AsRef<Path>>(_path: P) -> anyhow::Result<()> {
|
pub fn make_executable<P: AsRef<Path>>(_path: P) -> anyhow::Result<()> {
|
||||||
// TODO: test if this actually works
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
|
@ -44,8 +44,13 @@ impl Project {
|
||||||
|
|
||||||
let container_folder = node.container_folder(
|
let container_folder = node.container_folder(
|
||||||
&self
|
&self
|
||||||
.path()
|
.package_dir()
|
||||||
.join(node.base_folder(manifest.target.kind(), true))
|
.join(
|
||||||
|
manifest
|
||||||
|
.target
|
||||||
|
.kind()
|
||||||
|
.packages_folder(&node.pkg_ref.target_kind()),
|
||||||
|
)
|
||||||
.join(PACKAGES_CONTAINER_NAME),
|
.join(PACKAGES_CONTAINER_NAME),
|
||||||
name,
|
name,
|
||||||
version_id.version(),
|
version_id.version(),
|
||||||
|
|
99
src/lib.rs
99
src/lib.rs
|
@ -6,7 +6,7 @@
|
||||||
#[cfg(not(any(feature = "roblox", feature = "lune", feature = "luau")))]
|
#[cfg(not(any(feature = "roblox", feature = "lune", feature = "luau")))]
|
||||||
compile_error!("at least one of the features `roblox`, `lune`, or `luau` must be enabled");
|
compile_error!("at least one of the features `roblox`, `lune`, or `luau` must be enabled");
|
||||||
|
|
||||||
use crate::lockfile::Lockfile;
|
use crate::{lockfile::Lockfile, manifest::Manifest};
|
||||||
use gix::sec::identity::Account;
|
use gix::sec::identity::Account;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -108,7 +108,8 @@ impl AuthConfig {
|
||||||
/// The main struct of the pesde library, representing a project
|
/// The main struct of the pesde library, representing a project
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
path: PathBuf,
|
package_dir: PathBuf,
|
||||||
|
workspace_dir: Option<PathBuf>,
|
||||||
data_dir: PathBuf,
|
data_dir: PathBuf,
|
||||||
auth_config: AuthConfig,
|
auth_config: AuthConfig,
|
||||||
cas_dir: PathBuf,
|
cas_dir: PathBuf,
|
||||||
|
@ -116,23 +117,30 @@ pub struct Project {
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
/// Create a new `Project`
|
/// Create a new `Project`
|
||||||
pub fn new<P: AsRef<Path>, Q: AsRef<Path>, R: AsRef<Path>>(
|
pub fn new<P: AsRef<Path>, Q: AsRef<Path>, R: AsRef<Path>, S: AsRef<Path>>(
|
||||||
path: P,
|
package_dir: P,
|
||||||
data_dir: Q,
|
workspace_dir: Option<Q>,
|
||||||
cas_dir: R,
|
data_dir: R,
|
||||||
|
cas_dir: S,
|
||||||
auth_config: AuthConfig,
|
auth_config: AuthConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Project {
|
Project {
|
||||||
path: path.as_ref().to_path_buf(),
|
package_dir: package_dir.as_ref().to_path_buf(),
|
||||||
|
workspace_dir: workspace_dir.map(|d| d.as_ref().to_path_buf()),
|
||||||
data_dir: data_dir.as_ref().to_path_buf(),
|
data_dir: data_dir.as_ref().to_path_buf(),
|
||||||
auth_config,
|
auth_config,
|
||||||
cas_dir: cas_dir.as_ref().to_path_buf(),
|
cas_dir: cas_dir.as_ref().to_path_buf(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the path
|
/// Access the package directory
|
||||||
pub fn path(&self) -> &Path {
|
pub fn package_dir(&self) -> &Path {
|
||||||
&self.path
|
&self.package_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the workspace directory
|
||||||
|
pub fn workspace_dir(&self) -> Option<&Path> {
|
||||||
|
self.workspace_dir.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the data directory
|
/// Access the data directory
|
||||||
|
@ -152,37 +160,71 @@ impl Project {
|
||||||
|
|
||||||
/// Read the manifest file
|
/// Read the manifest file
|
||||||
pub fn read_manifest(&self) -> Result<String, errors::ManifestReadError> {
|
pub fn read_manifest(&self) -> Result<String, errors::ManifestReadError> {
|
||||||
let string = std::fs::read_to_string(self.path.join(MANIFEST_FILE_NAME))?;
|
let string = std::fs::read_to_string(self.package_dir.join(MANIFEST_FILE_NAME))?;
|
||||||
Ok(string)
|
Ok(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize the manifest file
|
/// Deserialize the manifest file
|
||||||
pub fn deser_manifest(&self) -> Result<manifest::Manifest, errors::ManifestReadError> {
|
pub fn deser_manifest(&self) -> Result<Manifest, errors::ManifestReadError> {
|
||||||
let string = std::fs::read_to_string(self.path.join(MANIFEST_FILE_NAME))?;
|
let string = std::fs::read_to_string(self.package_dir.join(MANIFEST_FILE_NAME))?;
|
||||||
Ok(toml::from_str(&string)?)
|
Ok(toml::from_str(&string)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the manifest file
|
/// Write the manifest file
|
||||||
pub fn write_manifest<S: AsRef<[u8]>>(&self, manifest: S) -> Result<(), std::io::Error> {
|
pub fn write_manifest<S: AsRef<[u8]>>(&self, manifest: S) -> Result<(), std::io::Error> {
|
||||||
std::fs::write(self.path.join(MANIFEST_FILE_NAME), manifest.as_ref())
|
std::fs::write(self.package_dir.join(MANIFEST_FILE_NAME), manifest.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize the lockfile
|
/// Deserialize the lockfile
|
||||||
pub fn deser_lockfile(&self) -> Result<Lockfile, errors::LockfileReadError> {
|
pub fn deser_lockfile(&self) -> Result<Lockfile, errors::LockfileReadError> {
|
||||||
let string = std::fs::read_to_string(self.path.join(LOCKFILE_FILE_NAME))?;
|
let string = std::fs::read_to_string(self.package_dir.join(LOCKFILE_FILE_NAME))?;
|
||||||
Ok(toml::from_str(&string)?)
|
Ok(toml::from_str(&string)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the lockfile
|
/// Write the lockfile
|
||||||
pub fn write_lockfile(&self, lockfile: Lockfile) -> Result<(), errors::LockfileWriteError> {
|
pub fn write_lockfile(&self, lockfile: Lockfile) -> Result<(), errors::LockfileWriteError> {
|
||||||
let string = toml::to_string(&lockfile)?;
|
let string = toml::to_string(&lockfile)?;
|
||||||
std::fs::write(self.path.join(LOCKFILE_FILE_NAME), string)?;
|
std::fs::write(self.package_dir.join(LOCKFILE_FILE_NAME), string)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the workspace members
|
||||||
|
pub fn workspace_members<P: AsRef<Path>>(
|
||||||
|
&self,
|
||||||
|
dir: P,
|
||||||
|
) -> Result<HashMap<PathBuf, Manifest>, errors::WorkspaceMembersError> {
|
||||||
|
let dir = dir.as_ref().to_path_buf();
|
||||||
|
let manifest = std::fs::read_to_string(dir.join(MANIFEST_FILE_NAME))
|
||||||
|
.map_err(|e| errors::WorkspaceMembersError::ManifestMissing(dir.to_path_buf(), e))?;
|
||||||
|
let manifest = toml::from_str::<Manifest>(&manifest)
|
||||||
|
.map_err(|e| errors::WorkspaceMembersError::ManifestDeser(dir.to_path_buf(), e))?;
|
||||||
|
|
||||||
|
let members = manifest
|
||||||
|
.workspace_members
|
||||||
|
.into_iter()
|
||||||
|
.map(|glob| dir.join(glob))
|
||||||
|
.map(|path| glob::glob(&path.as_os_str().to_string_lossy()))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|paths| paths.into_iter())
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
members
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| {
|
||||||
|
let manifest = std::fs::read_to_string(path.join(MANIFEST_FILE_NAME))
|
||||||
|
.map_err(|e| errors::WorkspaceMembersError::ManifestMissing(path.clone(), e))?;
|
||||||
|
let manifest = toml::from_str::<Manifest>(&manifest)
|
||||||
|
.map_err(|e| errors::WorkspaceMembersError::ManifestDeser(path.clone(), e))?;
|
||||||
|
Ok((path, manifest))
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can occur when using the pesde library
|
/// Errors that can occur when using the pesde library
|
||||||
pub mod errors {
|
pub mod errors {
|
||||||
|
use std::path::PathBuf;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Errors that can occur when reading the manifest file
|
/// Errors that can occur when reading the manifest file
|
||||||
|
@ -223,4 +265,29 @@ pub mod errors {
|
||||||
#[error("error serializing lockfile")]
|
#[error("error serializing lockfile")]
|
||||||
Serde(#[from] toml::ser::Error),
|
Serde(#[from] toml::ser::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors that can occur when finding workspace members
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum WorkspaceMembersError {
|
||||||
|
/// The manifest file could not be found
|
||||||
|
#[error("missing manifest file at {0}")]
|
||||||
|
ManifestMissing(PathBuf, #[source] std::io::Error),
|
||||||
|
|
||||||
|
/// An error occurred deserializing the manifest file
|
||||||
|
#[error("error deserializing manifest file at {0}")]
|
||||||
|
ManifestDeser(PathBuf, #[source] toml::de::Error),
|
||||||
|
|
||||||
|
/// An error occurred interacting with the filesystem
|
||||||
|
#[error("error interacting with the filesystem")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
/// An invalid glob pattern was found
|
||||||
|
#[error("invalid glob pattern")]
|
||||||
|
Glob(#[from] glob::PatternError),
|
||||||
|
|
||||||
|
/// An error occurred while globbing
|
||||||
|
#[error("error globbing")]
|
||||||
|
Globbing(#[from] glob::GlobError),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,10 +92,10 @@ pub fn get_lib_require_path(
|
||||||
) -> String {
|
) -> String {
|
||||||
let path = pathdiff::diff_paths(destination_dir, base_dir).unwrap();
|
let path = pathdiff::diff_paths(destination_dir, base_dir).unwrap();
|
||||||
let path = if use_new_structure {
|
let path = if use_new_structure {
|
||||||
log::debug!("using new structure for require path");
|
log::debug!("using new structure for require path with {:?}", lib_file);
|
||||||
lib_file.to_path(path)
|
lib_file.to_path(path)
|
||||||
} else {
|
} else {
|
||||||
log::debug!("using old structure for require path");
|
log::debug!("using old structure for require path with {:?}", lib_file);
|
||||||
path
|
path
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,13 @@ impl Project {
|
||||||
|
|
||||||
let container_folder = node.node.container_folder(
|
let container_folder = node.node.container_folder(
|
||||||
&self
|
&self
|
||||||
.path()
|
.package_dir()
|
||||||
.join(node.node.base_folder(manifest.target.kind(), true))
|
.join(
|
||||||
|
manifest
|
||||||
|
.target
|
||||||
|
.kind()
|
||||||
|
.packages_folder(&node.node.pkg_ref.target_kind()),
|
||||||
|
)
|
||||||
.join(PACKAGES_CONTAINER_NAME),
|
.join(PACKAGES_CONTAINER_NAME),
|
||||||
name,
|
name,
|
||||||
version_id.version(),
|
version_id.version(),
|
||||||
|
@ -99,7 +104,7 @@ impl Project {
|
||||||
|
|
||||||
execute_script(
|
execute_script(
|
||||||
ScriptName::RobloxSyncConfigGenerator,
|
ScriptName::RobloxSyncConfigGenerator,
|
||||||
&script_path.to_path(self.path()),
|
&script_path.to_path(self.package_dir()),
|
||||||
std::iter::once(container_folder.as_os_str())
|
std::iter::once(container_folder.as_os_str())
|
||||||
.chain(build_files.iter().map(OsStr::new)),
|
.chain(build_files.iter().map(OsStr::new)),
|
||||||
self,
|
self,
|
||||||
|
@ -117,56 +122,64 @@ 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 base_folder = create_and_canonicalize(
|
let node_container_folder = {
|
||||||
self.path().join(
|
let base_folder = create_and_canonicalize(
|
||||||
self.path()
|
self.package_dir().join(
|
||||||
.join(node.node.base_folder(manifest.target.kind(), true)),
|
manifest
|
||||||
),
|
.target
|
||||||
)?;
|
.kind()
|
||||||
let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME);
|
.packages_folder(&node.node.pkg_ref.target_kind()),
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME);
|
||||||
|
|
||||||
let container_folder = node.node.container_folder(
|
let container_folder = node.node.container_folder(
|
||||||
&packages_container_folder,
|
&packages_container_folder,
|
||||||
name,
|
name,
|
||||||
version_id.version(),
|
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_id))
|
.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))
|
||||||
if let Some(lib_file) = node.target.lib_path() {
|
})
|
||||||
write_cas(
|
{
|
||||||
base_folder.join(format!("{alias}.luau")),
|
if let Some(lib_file) = node.target.lib_path() {
|
||||||
self.cas_dir(),
|
write_cas(
|
||||||
&generator::generate_lib_linking_module(
|
base_folder.join(format!("{alias}.luau")),
|
||||||
&generator::get_lib_require_path(
|
self.cas_dir(),
|
||||||
&node.target.kind(),
|
&generator::generate_lib_linking_module(
|
||||||
&base_folder,
|
&generator::get_lib_require_path(
|
||||||
lib_file,
|
&node.target.kind(),
|
||||||
&container_folder,
|
&base_folder,
|
||||||
node.node.pkg_ref.use_new_structure(),
|
lib_file,
|
||||||
|
&container_folder,
|
||||||
|
node.node.pkg_ref.use_new_structure(),
|
||||||
|
),
|
||||||
|
types,
|
||||||
),
|
),
|
||||||
types,
|
)?;
|
||||||
),
|
};
|
||||||
)?;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(bin_file) = node.target.bin_path() {
|
if let Some(bin_file) = node.target.bin_path() {
|
||||||
write_cas(
|
write_cas(
|
||||||
base_folder.join(format!("{alias}.bin.luau")),
|
base_folder.join(format!("{alias}.bin.luau")),
|
||||||
self.cas_dir(),
|
self.cas_dir(),
|
||||||
&generator::generate_bin_linking_module(
|
&generator::generate_bin_linking_module(
|
||||||
&generator::get_bin_require_path(
|
&generator::get_bin_require_path(
|
||||||
&base_folder,
|
&base_folder,
|
||||||
bin_file,
|
bin_file,
|
||||||
&container_folder,
|
&container_folder,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)?;
|
||||||
)?;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
container_folder
|
||||||
|
};
|
||||||
|
|
||||||
for (dependency_name, (dependency_version_id, dependency_alias)) in
|
for (dependency_name, (dependency_version_id, dependency_alias)) in
|
||||||
&node.node.dependencies
|
&node.node.dependencies
|
||||||
|
@ -185,9 +198,19 @@ impl Project {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let packages_container_folder = create_and_canonicalize(
|
||||||
|
self.package_dir().join(
|
||||||
|
node.node
|
||||||
|
.pkg_ref
|
||||||
|
.target_kind()
|
||||||
|
.packages_folder(&dependency_node.node.pkg_ref.target_kind()),
|
||||||
|
),
|
||||||
|
)?
|
||||||
|
.join(PACKAGES_CONTAINER_NAME);
|
||||||
|
|
||||||
let linker_folder = create_and_canonicalize(
|
let linker_folder = create_and_canonicalize(
|
||||||
container_folder
|
node_container_folder
|
||||||
.join(dependency_node.node.base_folder(node.target.kind(), false)),
|
.join(node.node.base_folder(dependency_node.target.kind())),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
write_cas(
|
write_cas(
|
||||||
|
@ -203,7 +226,7 @@ impl Project {
|
||||||
dependency_name,
|
dependency_name,
|
||||||
dependency_version_id.version(),
|
dependency_version_id.version(),
|
||||||
),
|
),
|
||||||
node.node.pkg_ref.use_new_structure(),
|
dependency_node.node.pkg_ref.use_new_structure(),
|
||||||
),
|
),
|
||||||
package_types
|
package_types
|
||||||
.get(dependency_name)
|
.get(dependency_name)
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::{
|
||||||
version_id::VersionId,
|
version_id::VersionId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -36,10 +37,9 @@ pub struct DependencyGraphNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DependencyGraphNode {
|
impl DependencyGraphNode {
|
||||||
/// Returns the folder to store dependencies in for this package
|
pub(crate) fn base_folder(&self, project_target: TargetKind) -> String {
|
||||||
pub fn base_folder(&self, project_target: TargetKind, is_top_level: bool) -> String {
|
if self.pkg_ref.use_new_structure() {
|
||||||
if is_top_level || self.pkg_ref.use_new_structure() {
|
self.pkg_ref.target_kind().packages_folder(&project_target)
|
||||||
project_target.packages_folder(&self.pkg_ref.target_kind())
|
|
||||||
} else {
|
} else {
|
||||||
"..".to_string()
|
"..".to_string()
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,7 @@ impl DependencyGraphNode {
|
||||||
/// A graph of `DependencyGraphNode`s
|
/// A graph of `DependencyGraphNode`s
|
||||||
pub type DependencyGraph = Graph<DependencyGraphNode>;
|
pub type DependencyGraph = Graph<DependencyGraphNode>;
|
||||||
|
|
||||||
/// Inserts a node into a graph
|
pub(crate) fn insert_node(
|
||||||
pub fn insert_node(
|
|
||||||
graph: &mut DependencyGraph,
|
graph: &mut DependencyGraph,
|
||||||
name: PackageNames,
|
name: PackageNames,
|
||||||
version: VersionId,
|
version: VersionId,
|
||||||
|
@ -128,6 +127,10 @@ pub struct Lockfile {
|
||||||
#[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>,
|
||||||
|
|
||||||
|
/// The workspace members
|
||||||
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||||
|
pub workspace: BTreeMap<PackageName, RelativePathBuf>,
|
||||||
|
|
||||||
/// The graph of dependencies
|
/// The graph of dependencies
|
||||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||||
pub graph: DownloadedGraph,
|
pub graph: DownloadedGraph,
|
||||||
|
|
96
src/main.rs
96
src/main.rs
|
@ -1,12 +1,14 @@
|
||||||
use std::{fs::create_dir_all, path::PathBuf};
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use indicatif::MultiProgress;
|
use indicatif::MultiProgress;
|
||||||
use indicatif_log_bridge::LogWrapper;
|
use indicatif_log_bridge::LogWrapper;
|
||||||
|
|
||||||
use pesde::{AuthConfig, Project, MANIFEST_FILE_NAME};
|
use pesde::{AuthConfig, Project, MANIFEST_FILE_NAME};
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
fs::create_dir_all,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
auth::get_token,
|
auth::get_token,
|
||||||
|
@ -67,23 +69,6 @@ fn get_root(path: &std::path::Path) -> PathBuf {
|
||||||
|
|
||||||
fn run() -> anyhow::Result<()> {
|
fn run() -> anyhow::Result<()> {
|
||||||
let cwd = std::env::current_dir().expect("failed to get current working directory");
|
let cwd = std::env::current_dir().expect("failed to get current working directory");
|
||||||
let project_root_dir = 'finder: {
|
|
||||||
let mut project_root = cwd.clone();
|
|
||||||
|
|
||||||
while project_root.components().count() > 1 {
|
|
||||||
if project_root.join(MANIFEST_FILE_NAME).exists() {
|
|
||||||
break 'finder project_root;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(parent) = project_root.parent() {
|
|
||||||
project_root = parent.to_path_buf();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cwd.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
'scripts: {
|
'scripts: {
|
||||||
|
@ -105,17 +90,85 @@ fn run() -> anyhow::Result<()> {
|
||||||
break 'scripts;
|
break 'scripts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the bin script will search for the project root itself, so we do that to ensure
|
||||||
|
// consistency across platforms, since the script is executed using a shebang
|
||||||
|
// on unix systems
|
||||||
let status = std::process::Command::new("lune")
|
let status = std::process::Command::new("lune")
|
||||||
.arg("run")
|
.arg("run")
|
||||||
.arg(exe.with_extension(""))
|
.arg(exe.with_extension(""))
|
||||||
|
.arg("--")
|
||||||
.args(std::env::args_os().skip(1))
|
.args(std::env::args_os().skip(1))
|
||||||
.current_dir(project_root_dir)
|
.current_dir(cwd)
|
||||||
.status()
|
.status()
|
||||||
.expect("failed to run lune");
|
.expect("failed to run lune");
|
||||||
|
|
||||||
std::process::exit(status.code().unwrap());
|
std::process::exit(status.code().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (project_root_dir, project_workspace_dir) = 'finder: {
|
||||||
|
let mut current_path = Some(cwd.clone());
|
||||||
|
let mut project_root = None::<PathBuf>;
|
||||||
|
let mut workspace_dir = None::<PathBuf>;
|
||||||
|
|
||||||
|
fn get_workspace_members(path: &Path) -> anyhow::Result<HashSet<PathBuf>> {
|
||||||
|
let manifest = std::fs::read_to_string(path.join(MANIFEST_FILE_NAME))
|
||||||
|
.context("failed to read manifest")?;
|
||||||
|
let manifest: pesde::manifest::Manifest =
|
||||||
|
toml::from_str(&manifest).context("failed to parse manifest")?;
|
||||||
|
|
||||||
|
if manifest.workspace_members.is_empty() {
|
||||||
|
return Ok(HashSet::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest
|
||||||
|
.workspace_members
|
||||||
|
.iter()
|
||||||
|
.map(|member| path.join(member))
|
||||||
|
.map(|p| glob::glob(&p.to_string_lossy()))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.context("invalid glob patterns")?
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|paths| paths.into_iter())
|
||||||
|
.collect::<Result<HashSet<_>, _>>()
|
||||||
|
.context("failed to expand glob patterns")
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(path) = current_path {
|
||||||
|
current_path = path.parent().map(|p| p.to_path_buf());
|
||||||
|
|
||||||
|
if !path.join(MANIFEST_FILE_NAME).exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (project_root.as_ref(), workspace_dir.as_ref()) {
|
||||||
|
(Some(project_root), Some(workspace_dir)) => {
|
||||||
|
break 'finder (project_root.clone(), Some(workspace_dir.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
(Some(project_root), None) => {
|
||||||
|
if get_workspace_members(&path)?.contains(project_root) {
|
||||||
|
workspace_dir = Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(None, None) => {
|
||||||
|
if get_workspace_members(&path)?.contains(&cwd) {
|
||||||
|
// initializing a new member of a workspace
|
||||||
|
break 'finder (cwd, Some(path));
|
||||||
|
} else {
|
||||||
|
project_root = Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(None, Some(_)) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we mustn't expect the project root to be found, as that would
|
||||||
|
// disable the ability to run pesde in a non-project directory (for example to init it)
|
||||||
|
(project_root.unwrap_or_else(|| cwd.clone()), workspace_dir)
|
||||||
|
};
|
||||||
|
|
||||||
let multi = {
|
let multi = {
|
||||||
let logger = pretty_env_logger::formatted_builder()
|
let logger = pretty_env_logger::formatted_builder()
|
||||||
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"))
|
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"))
|
||||||
|
@ -143,6 +196,7 @@ fn run() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let project = Project::new(
|
let project = Project::new(
|
||||||
project_root_dir,
|
project_root_dir,
|
||||||
|
project_workspace_dir,
|
||||||
data_dir,
|
data_dir,
|
||||||
cas_dir,
|
cas_dir,
|
||||||
AuthConfig::new()
|
AuthConfig::new()
|
||||||
|
|
|
@ -74,6 +74,9 @@ pub struct Manifest {
|
||||||
#[serde(default, skip_serializing)]
|
#[serde(default, skip_serializing)]
|
||||||
/// Which version of the pesde CLI this package uses
|
/// Which version of the pesde CLI this package uses
|
||||||
pub pesde_version: Option<Version>,
|
pub pesde_version: Option<Version>,
|
||||||
|
/// A list of globs pointing to workspace members' directories
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub workspace_members: Vec<String>,
|
||||||
|
|
||||||
/// The standard dependencies of the package
|
/// The standard dependencies of the package
|
||||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::{lockfile::DownloadedGraph, Project, MANIFEST_FILE_NAME, PACKAGES_CONTAINER_NAME};
|
use crate::{
|
||||||
|
lockfile::DownloadedGraph, source::traits::PackageRef, Project, MANIFEST_FILE_NAME,
|
||||||
|
PACKAGES_CONTAINER_NAME,
|
||||||
|
};
|
||||||
use git2::{ApplyLocation, ApplyOptions, Diff, DiffFormat, DiffLineType, Repository, Signature};
|
use git2::{ApplyLocation, ApplyOptions, Diff, DiffFormat, DiffLineType, Repository, Signature};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
use std::{fs::read, path::Path};
|
use std::{fs::read, path::Path};
|
||||||
|
@ -73,7 +76,7 @@ impl Project {
|
||||||
|
|
||||||
for (name, versions) in manifest.patches {
|
for (name, versions) in manifest.patches {
|
||||||
for (version_id, patch_path) in versions {
|
for (version_id, patch_path) in versions {
|
||||||
let patch_path = patch_path.to_path(self.path());
|
let patch_path = patch_path.to_path(self.package_dir());
|
||||||
let patch = Diff::from_buffer(&read(&patch_path).map_err(|e| {
|
let patch = Diff::from_buffer(&read(&patch_path).map_err(|e| {
|
||||||
errors::ApplyPatchesError::PatchReadError(patch_path.clone(), e)
|
errors::ApplyPatchesError::PatchReadError(patch_path.clone(), e)
|
||||||
})?)?;
|
})?)?;
|
||||||
|
@ -87,8 +90,13 @@ impl Project {
|
||||||
|
|
||||||
let container_folder = node.node.container_folder(
|
let container_folder = node.node.container_folder(
|
||||||
&self
|
&self
|
||||||
.path()
|
.package_dir()
|
||||||
.join(node.node.base_folder(manifest.target.kind(), true))
|
.join(
|
||||||
|
manifest
|
||||||
|
.target
|
||||||
|
.kind()
|
||||||
|
.packages_folder(&node.node.pkg_ref.target_kind()),
|
||||||
|
)
|
||||||
.join(PACKAGES_CONTAINER_NAME),
|
.join(PACKAGES_CONTAINER_NAME),
|
||||||
&name,
|
&name,
|
||||||
version_id.version(),
|
version_id.version(),
|
||||||
|
|
|
@ -40,6 +40,11 @@ impl Project {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if matches!(specifier, DependencySpecifiers::Workspace(_)) {
|
||||||
|
// workspace dependencies must always be resolved brand new
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if all_specifiers
|
if all_specifiers
|
||||||
.remove(&(specifier.clone(), node.ty))
|
.remove(&(specifier.clone(), node.ty))
|
||||||
.is_none()
|
.is_none()
|
||||||
|
@ -180,6 +185,9 @@ impl Project {
|
||||||
DependencySpecifiers::Git(specifier) => PackageSources::Git(
|
DependencySpecifiers::Git(specifier) => PackageSources::Git(
|
||||||
crate::source::git::GitPackageSource::new(specifier.repo.clone()),
|
crate::source::git::GitPackageSource::new(specifier.repo.clone()),
|
||||||
),
|
),
|
||||||
|
DependencySpecifiers::Workspace(_) => {
|
||||||
|
PackageSources::Workspace(crate::source::workspace::WorkspacePackageSource)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if refreshed_sources.insert(source.clone()) {
|
if refreshed_sources.insert(source.clone()) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub(crate) fn execute_script<A: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||||
.arg(script_path.as_os_str())
|
.arg(script_path.as_os_str())
|
||||||
.arg("--")
|
.arg("--")
|
||||||
.args(args)
|
.args(args)
|
||||||
.current_dir(project.path())
|
.current_dir(project.package_dir())
|
||||||
.stdin(Stdio::inherit())
|
.stdin(Stdio::inherit())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
|
|
|
@ -4,12 +4,15 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
manifest::target::TargetKind,
|
||||||
|
source::{IGNORED_DIRS, IGNORED_FILES},
|
||||||
|
util::hash,
|
||||||
|
};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use crate::util::hash;
|
|
||||||
|
|
||||||
/// A file system entry
|
/// A file system entry
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum FSEntry {
|
pub enum FSEntry {
|
||||||
|
@ -23,8 +26,14 @@ pub enum FSEntry {
|
||||||
|
|
||||||
/// A package's file system
|
/// A package's file system
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
// don't need to differentiate between CAS and non-CAS, since non-CAS won't be serialized
|
||||||
pub struct PackageFS(pub(crate) BTreeMap<RelativePathBuf, FSEntry>);
|
#[serde(untagged)]
|
||||||
|
pub enum PackageFS {
|
||||||
|
/// A package stored in the CAS
|
||||||
|
CAS(BTreeMap<RelativePathBuf, FSEntry>),
|
||||||
|
/// A package that's to be copied
|
||||||
|
Copy(PathBuf, TargetKind),
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn store_in_cas<P: AsRef<Path>>(
|
pub(crate) fn store_in_cas<P: AsRef<Path>>(
|
||||||
cas_dir: P,
|
cas_dir: P,
|
||||||
|
@ -92,6 +101,40 @@ pub(crate) fn store_reader_in_cas<P: AsRef<Path>>(
|
||||||
Ok(hash)
|
Ok(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn copy_dir_all(
|
||||||
|
src: impl AsRef<Path>,
|
||||||
|
dst: impl AsRef<Path>,
|
||||||
|
target: TargetKind,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
std::fs::create_dir_all(&dst)?;
|
||||||
|
'outer: for entry in std::fs::read_dir(src)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let ty = entry.file_type()?;
|
||||||
|
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||||
|
|
||||||
|
if ty.is_dir() {
|
||||||
|
if IGNORED_DIRS.contains(&file_name.as_ref()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for other_target in TargetKind::VARIANTS {
|
||||||
|
if target.packages_folder(other_target) == file_name {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_dir_all(entry.path(), dst.as_ref().join(&file_name), target)?;
|
||||||
|
} else {
|
||||||
|
if IGNORED_FILES.contains(&file_name.as_ref()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::copy(entry.path(), dst.as_ref().join(file_name))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
impl PackageFS {
|
impl PackageFS {
|
||||||
/// Write the package to the given destination
|
/// Write the package to the given destination
|
||||||
pub fn write_to<P: AsRef<Path>, Q: AsRef<Path>>(
|
pub fn write_to<P: AsRef<Path>, Q: AsRef<Path>>(
|
||||||
|
@ -100,27 +143,34 @@ impl PackageFS {
|
||||||
cas_path: Q,
|
cas_path: Q,
|
||||||
link: bool,
|
link: bool,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
for (path, entry) in &self.0 {
|
match self {
|
||||||
let path = path.to_path(destination.as_ref());
|
PackageFS::CAS(entries) => {
|
||||||
|
for (path, entry) in entries {
|
||||||
|
let path = path.to_path(destination.as_ref());
|
||||||
|
|
||||||
match entry {
|
match entry {
|
||||||
FSEntry::File(hash) => {
|
FSEntry::File(hash) => {
|
||||||
if let Some(parent) = path.parent() {
|
if let Some(parent) = path.parent() {
|
||||||
std::fs::create_dir_all(parent)?;
|
std::fs::create_dir_all(parent)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (prefix, rest) = hash.split_at(2);
|
let (prefix, rest) = hash.split_at(2);
|
||||||
let cas_file_path = cas_path.as_ref().join(prefix).join(rest);
|
let cas_file_path = cas_path.as_ref().join(prefix).join(rest);
|
||||||
|
|
||||||
if link {
|
if link {
|
||||||
std::fs::hard_link(cas_file_path, path)?;
|
std::fs::hard_link(cas_file_path, path)?;
|
||||||
} else {
|
} else {
|
||||||
std::fs::copy(cas_file_path, path)?;
|
std::fs::copy(cas_file_path, path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FSEntry::Directory => {
|
||||||
|
std::fs::create_dir_all(path)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FSEntry::Directory => {
|
}
|
||||||
std::fs::create_dir_all(path)?;
|
PackageFS::Copy(src, target) => {
|
||||||
}
|
copy_dir_all(src, destination, *target)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +183,10 @@ impl PackageFS {
|
||||||
file_hash: H,
|
file_hash: H,
|
||||||
cas_path: P,
|
cas_path: P,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
|
if !matches!(self, PackageFS::CAS(_)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let (prefix, rest) = file_hash.as_ref().split_at(2);
|
let (prefix, rest) = file_hash.as_ref().split_at(2);
|
||||||
let cas_file_path = cas_path.as_ref().join(prefix).join(rest);
|
let cas_file_path = cas_path.as_ref().join(prefix).join(rest);
|
||||||
std::fs::read_to_string(cas_file_path).ok()
|
std::fs::read_to_string(cas_file_path).ok()
|
||||||
|
|
|
@ -162,6 +162,7 @@ impl PackageSource for GitPackageSource {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
DependencySpecifiers::Git(_) => {}
|
DependencySpecifiers::Git(_) => {}
|
||||||
|
DependencySpecifiers::Workspace(_) => todo!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((alias, (spec, ty)))
|
Ok((alias, (spec, ty)))
|
||||||
|
@ -262,21 +263,26 @@ impl PackageSource for GitPackageSource {
|
||||||
errors::DownloadError::DeserializeFile(Box::new(self.repo_url.clone()), e)
|
errors::DownloadError::DeserializeFile(Box::new(self.repo_url.clone()), e)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let manifest = match fs.0.get(&RelativePathBuf::from(MANIFEST_FILE_NAME)) {
|
let manifest = match &fs {
|
||||||
Some(FSEntry::File(hash)) => match fs
|
PackageFS::CAS(entries) => {
|
||||||
.read_file(hash, project.cas_dir())
|
match entries.get(&RelativePathBuf::from(MANIFEST_FILE_NAME)) {
|
||||||
.map(|m| toml::de::from_str::<Manifest>(&m))
|
Some(FSEntry::File(hash)) => match fs
|
||||||
{
|
.read_file(hash, project.cas_dir())
|
||||||
Some(Ok(m)) => Some(m),
|
.map(|m| toml::de::from_str::<Manifest>(&m))
|
||||||
Some(Err(e)) => {
|
{
|
||||||
return Err(errors::DownloadError::DeserializeFile(
|
Some(Ok(m)) => Some(m),
|
||||||
Box::new(self.repo_url.clone()),
|
Some(Err(e)) => {
|
||||||
e,
|
return Err(errors::DownloadError::DeserializeFile(
|
||||||
))
|
Box::new(self.repo_url.clone()),
|
||||||
|
e,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
None => None,
|
}
|
||||||
},
|
_ => unreachable!("the package fs should be CAS"),
|
||||||
_ => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = match manifest {
|
let target = match manifest {
|
||||||
|
@ -380,7 +386,7 @@ impl PackageSource for GitPackageSource {
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let fs = PackageFS(entries);
|
let fs = PackageFS::CAS(entries);
|
||||||
|
|
||||||
let target = match manifest {
|
let target = match manifest {
|
||||||
Some(manifest) => manifest.target,
|
Some(manifest) => manifest.target,
|
||||||
|
|
|
@ -29,6 +29,8 @@ pub mod version_id;
|
||||||
/// The Wally package source
|
/// The Wally package source
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
pub mod wally;
|
pub mod wally;
|
||||||
|
/// The workspace package source
|
||||||
|
pub mod workspace;
|
||||||
|
|
||||||
/// Files that will not be stored when downloading a package. These are only files which break pesde's functionality, or are meaningless and possibly heavy (e.g. `.DS_Store`)
|
/// Files that will not be stored when downloading a package. These are only files which break pesde's functionality, or are meaningless and possibly heavy (e.g. `.DS_Store`)
|
||||||
pub const IGNORED_FILES: &[&str] = &["foreman.toml", "aftman.toml", "rokit.toml", ".DS_Store"];
|
pub const IGNORED_FILES: &[&str] = &["foreman.toml", "aftman.toml", "rokit.toml", ".DS_Store"];
|
||||||
|
@ -49,6 +51,8 @@ pub enum PackageSources {
|
||||||
Wally(wally::WallyPackageSource),
|
Wally(wally::WallyPackageSource),
|
||||||
/// A Git package source
|
/// A Git package source
|
||||||
Git(git::GitPackageSource),
|
Git(git::GitPackageSource),
|
||||||
|
/// A workspace package source
|
||||||
|
Workspace(workspace::WorkspacePackageSource),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageSource for PackageSources {
|
impl PackageSource for PackageSources {
|
||||||
|
@ -64,6 +68,7 @@ impl PackageSource for PackageSources {
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
PackageSources::Wally(source) => source.refresh(project).map_err(Into::into),
|
PackageSources::Wally(source) => source.refresh(project).map_err(Into::into),
|
||||||
PackageSources::Git(source) => source.refresh(project).map_err(Into::into),
|
PackageSources::Git(source) => source.refresh(project).map_err(Into::into),
|
||||||
|
PackageSources::Workspace(source) => source.refresh(project).map_err(Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,11 +76,11 @@ impl PackageSource for PackageSources {
|
||||||
&self,
|
&self,
|
||||||
specifier: &Self::Specifier,
|
specifier: &Self::Specifier,
|
||||||
project: &Project,
|
project: &Project,
|
||||||
project_target: TargetKind,
|
package_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, project_target)
|
.resolve(specifier, project, package_target)
|
||||||
.map(|(name, results)| {
|
.map(|(name, results)| {
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
|
@ -89,7 +94,7 @@ impl PackageSource for PackageSources {
|
||||||
|
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
(PackageSources::Wally(source), DependencySpecifiers::Wally(specifier)) => source
|
(PackageSources::Wally(source), DependencySpecifiers::Wally(specifier)) => source
|
||||||
.resolve(specifier, project, project_target)
|
.resolve(specifier, project, package_target)
|
||||||
.map(|(name, results)| {
|
.map(|(name, results)| {
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
|
@ -102,7 +107,7 @@ impl PackageSource for PackageSources {
|
||||||
.map_err(Into::into),
|
.map_err(Into::into),
|
||||||
|
|
||||||
(PackageSources::Git(source), DependencySpecifiers::Git(specifier)) => source
|
(PackageSources::Git(source), DependencySpecifiers::Git(specifier)) => source
|
||||||
.resolve(specifier, project, project_target)
|
.resolve(specifier, project, package_target)
|
||||||
.map(|(name, results)| {
|
.map(|(name, results)| {
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
|
@ -114,6 +119,23 @@ impl PackageSource for PackageSources {
|
||||||
})
|
})
|
||||||
.map_err(Into::into),
|
.map_err(Into::into),
|
||||||
|
|
||||||
|
(PackageSources::Workspace(source), DependencySpecifiers::Workspace(specifier)) => {
|
||||||
|
source
|
||||||
|
.resolve(specifier, project, package_target)
|
||||||
|
.map(|(name, results)| {
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
results
|
||||||
|
.into_iter()
|
||||||
|
.map(|(version, pkg_ref)| {
|
||||||
|
(version, PackageRefs::Workspace(pkg_ref))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
_ => Err(errors::ResolveError::Mismatch),
|
_ => Err(errors::ResolveError::Mismatch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +160,10 @@ impl PackageSource for PackageSources {
|
||||||
.download(pkg_ref, project, reqwest)
|
.download(pkg_ref, project, reqwest)
|
||||||
.map_err(Into::into),
|
.map_err(Into::into),
|
||||||
|
|
||||||
|
(PackageSources::Workspace(source), PackageRefs::Workspace(pkg_ref)) => source
|
||||||
|
.download(pkg_ref, project, reqwest)
|
||||||
|
.map_err(Into::into),
|
||||||
|
|
||||||
_ => Err(errors::DownloadError::Mismatch),
|
_ => Err(errors::DownloadError::Mismatch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +180,10 @@ pub mod errors {
|
||||||
/// A git-based package source failed to refresh
|
/// A git-based package source failed to refresh
|
||||||
#[error("error refreshing pesde package source")]
|
#[error("error refreshing pesde package source")]
|
||||||
GitBased(#[from] crate::source::git_index::errors::RefreshError),
|
GitBased(#[from] crate::source::git_index::errors::RefreshError),
|
||||||
|
|
||||||
|
/// A workspace package source failed to refresh
|
||||||
|
#[error("error refreshing workspace package source")]
|
||||||
|
Workspace(#[from] crate::source::workspace::errors::RefreshError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can occur when resolving a package
|
/// Errors that can occur when resolving a package
|
||||||
|
@ -176,6 +206,10 @@ pub mod errors {
|
||||||
/// A Git package source failed to resolve
|
/// A Git package source failed to resolve
|
||||||
#[error("error resolving git package")]
|
#[error("error resolving git package")]
|
||||||
Git(#[from] crate::source::git::errors::ResolveError),
|
Git(#[from] crate::source::git::errors::ResolveError),
|
||||||
|
|
||||||
|
/// A workspace package source failed to resolve
|
||||||
|
#[error("error resolving workspace package")]
|
||||||
|
Workspace(#[from] crate::source::workspace::errors::ResolveError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can occur when downloading a package
|
/// Errors that can occur when downloading a package
|
||||||
|
@ -198,5 +232,9 @@ pub mod errors {
|
||||||
/// A Git package source failed to download
|
/// A Git package source failed to download
|
||||||
#[error("error downloading git package")]
|
#[error("error downloading git package")]
|
||||||
Git(#[from] crate::source::git::errors::DownloadError),
|
Git(#[from] crate::source::git::errors::DownloadError),
|
||||||
|
|
||||||
|
/// A workspace package source failed to download
|
||||||
|
#[error("error downloading workspace package")]
|
||||||
|
Workspace(#[from] crate::source::workspace::errors::DownloadError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,7 @@ impl PackageSource for PesdePackageSource {
|
||||||
&self,
|
&self,
|
||||||
specifier: &Self::Specifier,
|
specifier: &Self::Specifier,
|
||||||
project: &Project,
|
project: &Project,
|
||||||
project_target: TargetKind,
|
package_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, None) {
|
let string = match self.read_file([scope, name], project, None) {
|
||||||
|
@ -221,7 +221,7 @@ impl PackageSource for PesdePackageSource {
|
||||||
specifier.version.matches(version)
|
specifier.version.matches(version)
|
||||||
&& specifier
|
&& specifier
|
||||||
.target
|
.target
|
||||||
.map_or(project_target.is_compatible_with(target), |t| t == *target)
|
.map_or(package_target.is_compatible_with(target), |t| t == *target)
|
||||||
})
|
})
|
||||||
.map(|(id, entry)| {
|
.map(|(id, entry)| {
|
||||||
let version = id.version().clone();
|
let version = id.version().clone();
|
||||||
|
@ -331,7 +331,7 @@ impl PackageSource for PesdePackageSource {
|
||||||
entries.insert(path, FSEntry::File(hash));
|
entries.insert(path, FSEntry::File(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fs = PackageFS(entries);
|
let fs = PackageFS::CAS(entries);
|
||||||
|
|
||||||
if let Some(parent) = index_file.parent() {
|
if let Some(parent) = index_file.parent() {
|
||||||
std::fs::create_dir_all(parent)?;
|
std::fs::create_dir_all(parent)?;
|
||||||
|
|
|
@ -16,6 +16,8 @@ pub enum PackageRefs {
|
||||||
Wally(crate::source::wally::pkg_ref::WallyPackageRef),
|
Wally(crate::source::wally::pkg_ref::WallyPackageRef),
|
||||||
/// A Git package reference
|
/// A Git package reference
|
||||||
Git(crate::source::git::pkg_ref::GitPackageRef),
|
Git(crate::source::git::pkg_ref::GitPackageRef),
|
||||||
|
/// A workspace package reference
|
||||||
|
Workspace(crate::source::workspace::pkg_ref::WorkspacePackageRef),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageRefs {
|
impl PackageRefs {
|
||||||
|
@ -37,6 +39,7 @@ impl PackageRef for PackageRefs {
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
PackageRefs::Wally(pkg_ref) => pkg_ref.dependencies(),
|
PackageRefs::Wally(pkg_ref) => pkg_ref.dependencies(),
|
||||||
PackageRefs::Git(pkg_ref) => pkg_ref.dependencies(),
|
PackageRefs::Git(pkg_ref) => pkg_ref.dependencies(),
|
||||||
|
PackageRefs::Workspace(pkg_ref) => pkg_ref.dependencies(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +49,7 @@ impl PackageRef for PackageRefs {
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
PackageRefs::Wally(pkg_ref) => pkg_ref.use_new_structure(),
|
PackageRefs::Wally(pkg_ref) => pkg_ref.use_new_structure(),
|
||||||
PackageRefs::Git(pkg_ref) => pkg_ref.use_new_structure(),
|
PackageRefs::Git(pkg_ref) => pkg_ref.use_new_structure(),
|
||||||
|
PackageRefs::Workspace(pkg_ref) => pkg_ref.use_new_structure(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +59,7 @@ impl PackageRef for PackageRefs {
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
PackageRefs::Wally(pkg_ref) => pkg_ref.target_kind(),
|
PackageRefs::Wally(pkg_ref) => pkg_ref.target_kind(),
|
||||||
PackageRefs::Git(pkg_ref) => pkg_ref.target_kind(),
|
PackageRefs::Git(pkg_ref) => pkg_ref.target_kind(),
|
||||||
|
PackageRefs::Workspace(pkg_ref) => pkg_ref.target_kind(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +69,7 @@ impl PackageRef for PackageRefs {
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
PackageRefs::Wally(pkg_ref) => pkg_ref.source(),
|
PackageRefs::Wally(pkg_ref) => pkg_ref.source(),
|
||||||
PackageRefs::Git(pkg_ref) => pkg_ref.source(),
|
PackageRefs::Git(pkg_ref) => pkg_ref.source(),
|
||||||
|
PackageRefs::Workspace(pkg_ref) => pkg_ref.source(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ pub enum DependencySpecifiers {
|
||||||
Wally(crate::source::wally::specifier::WallyDependencySpecifier),
|
Wally(crate::source::wally::specifier::WallyDependencySpecifier),
|
||||||
/// A Git dependency specifier
|
/// A Git dependency specifier
|
||||||
Git(crate::source::git::specifier::GitDependencySpecifier),
|
Git(crate::source::git::specifier::GitDependencySpecifier),
|
||||||
|
/// A workspace dependency specifier
|
||||||
|
Workspace(crate::source::workspace::specifier::WorkspaceDependencySpecifier),
|
||||||
}
|
}
|
||||||
impl DependencySpecifier for DependencySpecifiers {}
|
impl DependencySpecifier for DependencySpecifiers {}
|
||||||
|
|
||||||
|
@ -23,6 +25,7 @@ impl Display for DependencySpecifiers {
|
||||||
#[cfg(feature = "wally-compat")]
|
#[cfg(feature = "wally-compat")]
|
||||||
DependencySpecifiers::Wally(specifier) => write!(f, "{specifier}"),
|
DependencySpecifiers::Wally(specifier) => write!(f, "{specifier}"),
|
||||||
DependencySpecifiers::Git(specifier) => write!(f, "{specifier}"),
|
DependencySpecifiers::Git(specifier) => write!(f, "{specifier}"),
|
||||||
|
DependencySpecifiers::Workspace(specifier) => write!(f, "{specifier}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub(crate) fn find_lib_path(
|
||||||
|
|
||||||
let result = execute_script(
|
let result = execute_script(
|
||||||
ScriptName::SourcemapGenerator,
|
ScriptName::SourcemapGenerator,
|
||||||
&script_path.to_path(&project.path),
|
&script_path.to_path(&project.package_dir),
|
||||||
[package_dir],
|
[package_dir],
|
||||||
project,
|
project,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -94,7 +94,7 @@ impl PackageSource for WallyPackageSource {
|
||||||
&self,
|
&self,
|
||||||
specifier: &Self::Specifier,
|
specifier: &Self::Specifier,
|
||||||
project: &Project,
|
project: &Project,
|
||||||
_project_target: TargetKind,
|
_package_target: TargetKind,
|
||||||
) -> Result<crate::source::ResolveResult<Self::Ref>, Self::ResolveError> {
|
) -> Result<crate::source::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, None) {
|
let string = match self.read_file([scope, name], project, None) {
|
||||||
|
@ -238,7 +238,7 @@ impl PackageSource for WallyPackageSource {
|
||||||
entries.insert(path, FSEntry::File(hash));
|
entries.insert(path, FSEntry::File(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fs = PackageFS(entries);
|
let fs = PackageFS::CAS(entries);
|
||||||
|
|
||||||
if let Some(parent) = index_file.parent() {
|
if let Some(parent) = index_file.parent() {
|
||||||
std::fs::create_dir_all(parent).map_err(errors::DownloadError::WriteIndex)?;
|
std::fs::create_dir_all(parent).map_err(errors::DownloadError::WriteIndex)?;
|
||||||
|
|
173
src/source/workspace/mod.rs
Normal file
173
src/source/workspace/mod.rs
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
use crate::{
|
||||||
|
manifest::target::{Target, TargetKind},
|
||||||
|
names::PackageNames,
|
||||||
|
source::{
|
||||||
|
fs::PackageFS, specifiers::DependencySpecifiers, traits::PackageSource,
|
||||||
|
version_id::VersionId, workspace::pkg_ref::WorkspacePackageRef, ResolveResult,
|
||||||
|
},
|
||||||
|
Project, DEFAULT_INDEX_NAME,
|
||||||
|
};
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
/// The workspace package reference
|
||||||
|
pub mod pkg_ref;
|
||||||
|
/// The workspace dependency specifier
|
||||||
|
pub mod specifier;
|
||||||
|
|
||||||
|
/// The workspace package source
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct WorkspacePackageSource;
|
||||||
|
|
||||||
|
impl PackageSource for WorkspacePackageSource {
|
||||||
|
type Specifier = specifier::WorkspaceDependencySpecifier;
|
||||||
|
type Ref = WorkspacePackageRef;
|
||||||
|
type RefreshError = errors::RefreshError;
|
||||||
|
type ResolveError = errors::ResolveError;
|
||||||
|
type DownloadError = errors::DownloadError;
|
||||||
|
|
||||||
|
fn refresh(&self, _project: &Project) -> Result<(), Self::RefreshError> {
|
||||||
|
// no-op
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
specifier: &Self::Specifier,
|
||||||
|
project: &Project,
|
||||||
|
_package_target: TargetKind,
|
||||||
|
) -> Result<ResolveResult<Self::Ref>, Self::ResolveError> {
|
||||||
|
let (path, manifest) = 'finder: {
|
||||||
|
let workspace_dir = project
|
||||||
|
.workspace_dir
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&project.package_dir);
|
||||||
|
|
||||||
|
for (path, manifest) in project.workspace_members(workspace_dir)? {
|
||||||
|
if manifest.name == specifier.name {
|
||||||
|
break 'finder (path, manifest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(errors::ResolveError::NoWorkspaceMember(
|
||||||
|
specifier.name.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
PackageNames::Pesde(manifest.name.clone()),
|
||||||
|
BTreeMap::from([(
|
||||||
|
VersionId::new(manifest.version.clone(), manifest.target.kind()),
|
||||||
|
WorkspacePackageRef {
|
||||||
|
// workspace_dir is guaranteed to be Some by the workspace_members method
|
||||||
|
// strip_prefix is guaranteed to be Some by same method
|
||||||
|
// from_path is guaranteed to be Ok because we just stripped the absolute path
|
||||||
|
path: RelativePathBuf::from_path(
|
||||||
|
path.strip_prefix(project.workspace_dir.clone().unwrap())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
dependencies: manifest
|
||||||
|
.all_dependencies()?
|
||||||
|
.into_iter()
|
||||||
|
.map(|(alias, (mut spec, ty))| {
|
||||||
|
match &mut spec {
|
||||||
|
DependencySpecifiers::Pesde(spec) => {
|
||||||
|
let index_name =
|
||||||
|
spec.index.as_deref().unwrap_or(DEFAULT_INDEX_NAME);
|
||||||
|
|
||||||
|
spec.index = Some(
|
||||||
|
manifest
|
||||||
|
.indices
|
||||||
|
.get(index_name)
|
||||||
|
.ok_or(errors::ResolveError::IndexNotFound(
|
||||||
|
index_name.to_string(),
|
||||||
|
manifest.name.to_string(),
|
||||||
|
))?
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "wally-compat")]
|
||||||
|
DependencySpecifiers::Wally(spec) => {
|
||||||
|
let index_name =
|
||||||
|
spec.index.as_deref().unwrap_or(DEFAULT_INDEX_NAME);
|
||||||
|
|
||||||
|
spec.index = Some(
|
||||||
|
manifest
|
||||||
|
.wally_indices
|
||||||
|
.get(index_name)
|
||||||
|
.ok_or(errors::ResolveError::IndexNotFound(
|
||||||
|
index_name.to_string(),
|
||||||
|
manifest.name.to_string(),
|
||||||
|
))?
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DependencySpecifiers::Git(_) => {}
|
||||||
|
DependencySpecifiers::Workspace(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((alias, (spec, ty)))
|
||||||
|
})
|
||||||
|
.collect::<Result<_, errors::ResolveError>>()?,
|
||||||
|
target: manifest.target,
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download(
|
||||||
|
&self,
|
||||||
|
pkg_ref: &Self::Ref,
|
||||||
|
project: &Project,
|
||||||
|
_reqwest: &Client,
|
||||||
|
) -> Result<(PackageFS, Target), Self::DownloadError> {
|
||||||
|
let path = pkg_ref.path.to_path(project.workspace_dir.clone().unwrap());
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
PackageFS::Copy(path, pkg_ref.target.kind()),
|
||||||
|
pkg_ref.target.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors that can occur when using a workspace package source
|
||||||
|
pub mod errors {
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Errors that can occur when refreshing the workspace package source
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum RefreshError {}
|
||||||
|
|
||||||
|
/// Errors that can occur when resolving a workspace package
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ResolveError {
|
||||||
|
/// An error occurred reading the workspace members
|
||||||
|
#[error("failed to read workspace members")]
|
||||||
|
ReadWorkspaceMembers(#[from] crate::errors::WorkspaceMembersError),
|
||||||
|
|
||||||
|
/// No workspace member was found with the given name
|
||||||
|
#[error("no workspace member found with name {0}")]
|
||||||
|
NoWorkspaceMember(String),
|
||||||
|
|
||||||
|
/// An error occurred getting all dependencies
|
||||||
|
#[error("failed to get all dependencies")]
|
||||||
|
AllDependencies(#[from] crate::manifest::errors::AllDependenciesError),
|
||||||
|
|
||||||
|
/// An index of a member package was not found
|
||||||
|
#[error("index {0} not found in member {1}")]
|
||||||
|
IndexNotFound(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors that can occur when downloading a workspace package
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum DownloadError {
|
||||||
|
/// An error occurred reading the workspace members
|
||||||
|
#[error("failed to read workspace members")]
|
||||||
|
ReadWorkspaceMembers(#[from] std::io::Error),
|
||||||
|
}
|
||||||
|
}
|
40
src/source/workspace/pkg_ref.rs
Normal file
40
src/source/workspace/pkg_ref.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
manifest::{
|
||||||
|
target::{Target, TargetKind},
|
||||||
|
DependencyType,
|
||||||
|
},
|
||||||
|
source::{workspace::WorkspacePackageSource, DependencySpecifiers, PackageRef, PackageSources},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A workspace package reference
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
pub struct WorkspacePackageRef {
|
||||||
|
/// The path of the package
|
||||||
|
pub path: RelativePathBuf,
|
||||||
|
/// The dependencies of the package
|
||||||
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||||
|
pub dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>,
|
||||||
|
/// The target of the package
|
||||||
|
pub target: Target,
|
||||||
|
}
|
||||||
|
impl PackageRef for WorkspacePackageRef {
|
||||||
|
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)> {
|
||||||
|
&self.dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_new_structure(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_kind(&self) -> TargetKind {
|
||||||
|
self.target.kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> PackageSources {
|
||||||
|
PackageSources::Workspace(WorkspacePackageSource)
|
||||||
|
}
|
||||||
|
}
|
78
src/source/workspace/specifier.rs
Normal file
78
src/source/workspace/specifier.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use crate::{names::PackageName, source::DependencySpecifier};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
/// The specifier for a workspace dependency
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct WorkspaceDependencySpecifier {
|
||||||
|
/// The name of the workspace package
|
||||||
|
#[serde(rename = "workspace")]
|
||||||
|
pub name: PackageName,
|
||||||
|
/// The version type to use when publishing the package
|
||||||
|
#[serde(default, rename = "version")]
|
||||||
|
pub version_type: VersionType,
|
||||||
|
}
|
||||||
|
impl DependencySpecifier for WorkspaceDependencySpecifier {}
|
||||||
|
|
||||||
|
impl Display for WorkspaceDependencySpecifier {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "workspace:{}{}", self.version_type, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of version to use when publishing a package
|
||||||
|
#[derive(
|
||||||
|
Debug, SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Default,
|
||||||
|
)]
|
||||||
|
pub enum VersionType {
|
||||||
|
/// The "^" version type
|
||||||
|
#[default]
|
||||||
|
Caret,
|
||||||
|
/// The "~" version type
|
||||||
|
Tilde,
|
||||||
|
/// The "=" version type
|
||||||
|
Exact,
|
||||||
|
/// The "*" version type
|
||||||
|
Wildcard,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for VersionType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
VersionType::Caret => write!(f, "^"),
|
||||||
|
VersionType::Tilde => write!(f, "~"),
|
||||||
|
VersionType::Exact => write!(f, "="),
|
||||||
|
VersionType::Wildcard => write!(f, "*"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for VersionType {
|
||||||
|
type Err = errors::VersionTypeFromStr;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"^" => Ok(VersionType::Caret),
|
||||||
|
"~" => Ok(VersionType::Tilde),
|
||||||
|
"=" => Ok(VersionType::Exact),
|
||||||
|
"*" => Ok(VersionType::Wildcard),
|
||||||
|
_ => Err(errors::VersionTypeFromStr::InvalidVersionType(
|
||||||
|
s.to_string(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors that can occur when using a version type
|
||||||
|
pub mod errors {
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Errors that can occur when parsing a version type
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum VersionTypeFromStr {
|
||||||
|
/// The version type is invalid
|
||||||
|
#[error("invalid version type: {0}")]
|
||||||
|
InvalidVersionType(String),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue