feat: remove data redundancy for workspace pkg refs

This commit is contained in:
daimond113 2024-12-30 19:06:53 +01:00
parent c3d2c768db
commit 2700fe9e07
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
7 changed files with 110 additions and 107 deletions

View file

@ -10,9 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improve installation experience by @lukadev-0 - Improve installation experience by @lukadev-0
- Support using aliases of own dependencies for overrides by @daimond113 - Support using aliases of own dependencies for overrides by @daimond113
- Support ignoring parse errors in Luau files by @daimond113 - Support ignoring parse errors in Luau files by @daimond113
- Add path dependencies by @daimond113
### Removed ### Removed
- Remove old includes format compatibility by @daimond113 - Remove old includes format compatibility by @daimond113
- Remove data redundacy for workspace package references by @daimond113
### Performance ### Performance
- Use `Arc` for more efficient cloning of multiple structs by @daimond113 - Use `Arc` for more efficient cloning of multiple structs by @daimond113

View file

@ -3,6 +3,7 @@ use anyhow::Context;
use clap::Args; use clap::Args;
use futures::{StreamExt, TryStreamExt}; use futures::{StreamExt, TryStreamExt};
use pesde::{ use pesde::{
errors::{ManifestReadError, WorkspaceMembersError},
linking::generator::generate_bin_linking_module, linking::generator::generate_bin_linking_module,
names::{PackageName, PackageNames}, names::{PackageName, PackageNames},
Project, MANIFEST_FILE_NAME, PACKAGES_CONTAINER_NAME, Project, MANIFEST_FILE_NAME, PACKAGES_CONTAINER_NAME,
@ -124,9 +125,9 @@ impl RunCommand {
.workspace_dir() .workspace_dir()
.unwrap_or_else(|| project.package_dir()); .unwrap_or_else(|| project.package_dir());
let members = match project.workspace_members(workspace_dir, false).await { let members = match project.workspace_members(false).await {
Ok(members) => members.boxed(), Ok(members) => members.boxed(),
Err(pesde::errors::WorkspaceMembersError::ManifestMissing(e)) Err(WorkspaceMembersError::ManifestParse(ManifestReadError::Io(e)))
if e.kind() == std::io::ErrorKind::NotFound => if e.kind() == std::io::ErrorKind::NotFound =>
{ {
futures::stream::empty().boxed() futures::stream::empty().boxed()

View file

@ -258,9 +258,7 @@ pub async fn run_on_workspace_members<F: Future<Output = anyhow::Result<()>>>(
return Ok(Default::default()); return Ok(Default::default());
} }
let members_future = project let members_future = project.workspace_members(true).await?;
.workspace_members(project.package_dir(), true)
.await?;
pin!(members_future); pin!(members_future);
let mut results = BTreeMap::<PackageName, BTreeMap<TargetKind, RelativePathBuf>>::new(); let mut results = BTreeMap::<PackageName, BTreeMap<TargetKind, RelativePathBuf>>::new();

View file

@ -215,21 +215,15 @@ impl Project {
/// Get the workspace members /// Get the workspace members
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
pub async fn workspace_members<P: AsRef<Path> + Debug>( pub async fn workspace_members(
&self, &self,
dir: P,
can_ref_self: bool, can_ref_self: bool,
) -> Result< ) -> Result<
impl Stream<Item = Result<(PathBuf, Manifest), errors::WorkspaceMembersError>>, impl Stream<Item = Result<(PathBuf, Manifest), errors::WorkspaceMembersError>>,
errors::WorkspaceMembersError, errors::WorkspaceMembersError,
> { > {
let dir = dir.as_ref().to_path_buf(); let dir = self.workspace_dir().unwrap_or(self.package_dir());
let manifest = fs::read_to_string(dir.join(MANIFEST_FILE_NAME)) let manifest = deser_manifest(dir).await?;
.await
.map_err(errors::WorkspaceMembersError::ManifestMissing)?;
let manifest = toml::from_str::<Manifest>(&manifest).map_err(|e| {
errors::WorkspaceMembersError::ManifestDeser(dir.to_path_buf(), Box::new(e))
})?;
let members = matching_globs( let members = matching_globs(
dir, dir,
@ -241,13 +235,7 @@ impl Project {
Ok(try_stream! { Ok(try_stream! {
for path in members { for path in members {
let manifest = fs::read_to_string(path.join(MANIFEST_FILE_NAME)) let manifest = deser_manifest(&path).await?;
.await
.map_err(errors::WorkspaceMembersError::ManifestMissing)?;
let manifest = toml::from_str::<Manifest>(&manifest).map_err(|e| {
errors::WorkspaceMembersError::ManifestDeser(path.clone(), Box::new(e))
})?;
yield (path, manifest); yield (path, manifest);
} }
}) })
@ -346,7 +334,70 @@ impl RefreshedSources {
async fn deser_manifest(path: &Path) -> Result<Manifest, errors::ManifestReadError> { async fn deser_manifest(path: &Path) -> Result<Manifest, errors::ManifestReadError> {
let string = fs::read_to_string(path.join(MANIFEST_FILE_NAME)).await?; let string = fs::read_to_string(path.join(MANIFEST_FILE_NAME)).await?;
Ok(toml::from_str(&string)?) toml::from_str(&string).map_err(|e| errors::ManifestReadError::Serde(path.to_path_buf(), e))
}
/// Find the project & workspace directory roots
pub async fn find_roots(
cwd: PathBuf,
) -> Result<(PathBuf, Option<PathBuf>), errors::FindRootsError> {
let mut current_path = Some(cwd.clone());
let mut project_root = None::<PathBuf>;
let mut workspace_dir = None::<PathBuf>;
async fn get_workspace_members(
path: &Path,
) -> Result<HashSet<PathBuf>, errors::FindRootsError> {
let manifest = deser_manifest(path).await?;
if manifest.workspace_members.is_empty() {
return Ok(HashSet::new());
}
matching_globs(
path,
manifest.workspace_members.iter().map(|s| s.as_str()),
false,
false,
)
.await
.map_err(errors::FindRootsError::Globbing)
}
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)) => {
return Ok((project_root.clone(), Some(workspace_dir.clone())));
}
(Some(project_root), None) => {
if get_workspace_members(&path).await?.contains(project_root) {
workspace_dir = Some(path);
}
}
(None, None) => {
if get_workspace_members(&path).await?.contains(&cwd) {
// initializing a new member of a workspace
return Ok((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)
Ok((project_root.unwrap_or(cwd), workspace_dir))
} }
/// Errors that can occur when using the pesde library /// Errors that can occur when using the pesde library
@ -363,8 +414,8 @@ pub mod errors {
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
/// An error occurred while deserializing the manifest file /// An error occurred while deserializing the manifest file
#[error("error deserializing manifest file")] #[error("error deserializing manifest file at {0}")]
Serde(#[from] toml::de::Error), Serde(PathBuf, #[source] toml::de::Error),
} }
/// Errors that can occur when reading the lockfile /// Errors that can occur when reading the lockfile
@ -397,13 +448,9 @@ pub mod errors {
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum WorkspaceMembersError { pub enum WorkspaceMembersError {
/// The manifest file could not be found /// An error occurred parsing the manifest file
#[error("missing manifest file")] #[error("error parsing manifest file")]
ManifestMissing(#[source] std::io::Error), ManifestParse(#[from] ManifestReadError),
/// An error occurred deserializing the manifest file
#[error("error deserializing manifest file at {0}")]
ManifestDeser(PathBuf, #[source] Box<toml::de::Error>),
/// An error occurred interacting with the filesystem /// An error occurred interacting with the filesystem
#[error("error interacting with the filesystem")] #[error("error interacting with the filesystem")]
@ -426,4 +473,17 @@ pub mod errors {
#[error("error building glob")] #[error("error building glob")]
BuildGlob(#[from] wax::BuildError), BuildGlob(#[from] wax::BuildError),
} }
/// Errors that can occur when finding project roots
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum FindRootsError {
/// Reading the manifest failed
#[error("error reading manifest")]
ManifestRead(#[from] ManifestReadError),
/// Globbing failed
#[error("error globbing")]
Globbing(#[from] MatchingGlobsError),
}
} }

View file

@ -5,9 +5,8 @@ use anyhow::Context;
use clap::{builder::styling::AnsiColor, Parser}; use clap::{builder::styling::AnsiColor, Parser};
use fs_err::tokio as fs; use fs_err::tokio as fs;
use indicatif::MultiProgress; use indicatif::MultiProgress;
use pesde::{matching_globs, AuthConfig, Project, MANIFEST_FILE_NAME}; use pesde::{find_roots, AuthConfig, Project};
use std::{ use std::{
collections::HashSet,
io, io,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Mutex, sync::Mutex,
@ -208,67 +207,9 @@ async fn run() -> anyhow::Result<()> {
.with(fmt_layer) .with(fmt_layer)
.init(); .init();
let (project_root_dir, project_workspace_dir) = 'finder: { let (project_root_dir, project_workspace_dir) = find_roots(cwd.clone())
let mut current_path = Some(cwd.clone()); .await
let mut project_root = None::<PathBuf>; .context("failed to find project root")?;
let mut workspace_dir = None::<PathBuf>;
async fn get_workspace_members(path: &Path) -> anyhow::Result<HashSet<PathBuf>> {
let manifest = fs::read_to_string(path.join(MANIFEST_FILE_NAME))
.await
.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());
}
matching_globs(
path,
manifest.workspace_members.iter().map(|s| s.as_str()),
false,
false,
)
.await
.context("failed to get workspace members")
}
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).await?.contains(project_root) {
workspace_dir = Some(path);
}
}
(None, None) => {
if get_workspace_members(&path).await?.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)
};
tracing::trace!( tracing::trace!(
"project root: {}\nworkspace root: {}", "project root: {}\nworkspace root: {}",

View file

@ -1,4 +1,5 @@
use crate::{ use crate::{
deser_manifest,
manifest::target::Target, manifest::target::Target,
names::PackageNames, names::PackageNames,
reporters::DownloadProgressReporter, reporters::DownloadProgressReporter,
@ -47,10 +48,9 @@ impl PackageSource for WorkspacePackageSource {
} = options; } = options;
let (path, manifest) = 'finder: { let (path, manifest) = 'finder: {
let workspace_dir = project.workspace_dir().unwrap_or(project.package_dir());
let target = specifier.target.unwrap_or(*project_target); let target = specifier.target.unwrap_or(*project_target);
let members = project.workspace_members(workspace_dir, true).await?; let members = project.workspace_members(true).await?;
pin!(members); pin!(members);
while let Some((path, manifest)) = members.next().await.transpose()? { while let Some((path, manifest)) = members.next().await.transpose()? {
@ -71,7 +71,8 @@ impl PackageSource for WorkspacePackageSource {
// strip_prefix is guaranteed to be Some by same 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 // from_path is guaranteed to be Ok because we just stripped the absolute path
path: RelativePathBuf::from_path( path: RelativePathBuf::from_path(
path.strip_prefix(project.workspace_dir().unwrap()).unwrap(), path.strip_prefix(project.workspace_dir().unwrap_or(project.package_dir()))
.unwrap(),
) )
.unwrap(), .unwrap(),
dependencies: manifest dependencies: manifest
@ -116,7 +117,6 @@ impl PackageSource for WorkspacePackageSource {
Ok((alias, (spec, ty))) Ok((alias, (spec, ty)))
}) })
.collect::<Result<_, errors::ResolveError>>()?, .collect::<Result<_, errors::ResolveError>>()?,
target: manifest.target,
}; };
Ok(( Ok((
@ -136,11 +136,14 @@ impl PackageSource for WorkspacePackageSource {
) -> Result<(PackageFS, Target), Self::DownloadError> { ) -> Result<(PackageFS, Target), Self::DownloadError> {
let DownloadOptions { project, .. } = options; let DownloadOptions { project, .. } = options;
let path = pkg_ref.path.to_path(project.workspace_dir().unwrap()); let path = pkg_ref
.path
.to_path(project.workspace_dir().unwrap_or(project.package_dir()));
let manifest = deser_manifest(&path).await?;
Ok(( Ok((
PackageFS::Copy(path, pkg_ref.target.kind()), PackageFS::Copy(path, manifest.target.kind()),
pkg_ref.target.clone(), manifest.target,
)) ))
} }
} }
@ -180,8 +183,8 @@ pub mod errors {
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum DownloadError { pub enum DownloadError {
/// An error occurred reading the workspace members /// Reading the manifest failed
#[error("failed to read workspace members")] #[error("error reading manifest")]
ReadWorkspaceMembers(#[from] std::io::Error), ManifestRead(#[from] crate::errors::ManifestReadError),
} }
} }

View file

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::{ use crate::{
manifest::{target::Target, DependencyType}, manifest::DependencyType,
source::{workspace::WorkspacePackageSource, DependencySpecifiers, PackageRef, PackageSources}, source::{workspace::WorkspacePackageSource, DependencySpecifiers, PackageRef, PackageSources},
}; };
@ -15,8 +15,6 @@ pub struct WorkspacePackageRef {
/// The dependencies of the package /// The dependencies of the package
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>, pub dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>,
/// The target of the package
pub target: Target,
} }
impl PackageRef for WorkspacePackageRef { impl PackageRef for WorkspacePackageRef {
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)> { fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)> {