From b8c4f7486b96b7ebb3dafe476f19f846f3076628 Mon Sep 17 00:00:00 2001
From: daimond113 <contact@daimond113.com>
Date: Sun, 9 Mar 2025 17:41:38 +0100
Subject: [PATCH] refactor: switch from sync Path::exists() method

This commit disallows the method through clippy
and switches to the async equivalents, as to not
block the async runtime.
---
 clippy.toml                 |  3 +++
 src/cli/commands/publish.rs |  4 ++--
 src/cli/commands/run.rs     |  7 +++++--
 src/lib.rs                  | 38 +++++++++++++++++++++++++++----------
 src/patches.rs              |  1 +
 5 files changed, 39 insertions(+), 14 deletions(-)

diff --git a/clippy.toml b/clippy.toml
index cda8d17..1f754d0 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -1 +1,4 @@
 avoid-breaking-exported-api = false
+disallowed-methods = [
+    "std::path::Path::exists"
+]
\ No newline at end of file
diff --git a/src/cli/commands/publish.rs b/src/cli/commands/publish.rs
index fdb3a6a..78cf6c7 100644
--- a/src/cli/commands/publish.rs
+++ b/src/cli/commands/publish.rs
@@ -347,7 +347,7 @@ info: otherwise, the file was deemed unnecessary, if you don't understand why, p
 
 				let build_file_path = project.package_dir().join(build_file);
 
-				if !build_file_path.exists() {
+				if fs::metadata(&build_file_path).await.is_err() {
 					anyhow::bail!("build file {build_file} does not exist");
 				}
 
@@ -400,7 +400,7 @@ info: otherwise, the file was deemed unnecessary, if you don't understand why, p
 		for relative_path in &paths {
 			let path = project.package_dir().join(relative_path);
 
-			if !path.exists() {
+			if fs::metadata(&path).await.is_err() {
 				anyhow::bail!("included file `{}` does not exist", path.display());
 			}
 
diff --git a/src/cli/commands/run.rs b/src/cli/commands/run.rs
index 53143e6..26174ca 100644
--- a/src/cli/commands/run.rs
+++ b/src/cli/commands/run.rs
@@ -1,6 +1,7 @@
 use crate::cli::{style::WARN_STYLE, up_to_date_lockfile};
 use anyhow::Context as _;
 use clap::Args;
+use fs_err::tokio as fs;
 use futures::{StreamExt as _, TryStreamExt as _};
 use pesde::{
 	errors::{ManifestReadError, WorkspaceMembersError},
@@ -154,7 +155,7 @@ impl RunCommand {
 		let relative_path = RelativePathBuf::from(package_or_script);
 		let path = relative_path.to_path(project.package_dir());
 
-		if !path.exists() {
+		if fs::metadata(&path).await.is_err() {
 			anyhow::bail!("path `{}` does not exist", path.display());
 		}
 
@@ -194,7 +195,9 @@ impl RunCommand {
 					.context("failed to canonicalize parent")?;
 
 				if members.contains(&canonical_path)
-					&& canonical_path.join(MANIFEST_FILE_NAME).exists()
+					&& fs::metadata(canonical_path.join(MANIFEST_FILE_NAME))
+						.await
+						.is_ok()
 				{
 					break 'finder canonical_path;
 				}
diff --git a/src/lib.rs b/src/lib.rs
index c34b708..a8ccc6b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -23,6 +23,7 @@ use std::{
 	path::{Path, PathBuf},
 	sync::Arc,
 };
+use tokio::io::AsyncReadExt as _;
 use tracing::instrument;
 use wax::Pattern as _;
 
@@ -396,9 +397,16 @@ pub async fn find_roots(
 	let mut workspace_dir = None::<PathBuf>;
 
 	async fn get_workspace_members(
+		manifest_file: &mut fs::File,
 		path: &Path,
 	) -> Result<HashSet<PathBuf>, errors::FindRootsError> {
-		let manifest = deser_manifest(path).await?;
+		let mut manifest = String::new();
+		manifest_file
+			.read_to_string(&mut manifest)
+			.await
+			.map_err(errors::ManifestReadError::Io)?;
+		let manifest: Manifest = toml::from_str(&manifest)
+			.map_err(|e| errors::ManifestReadError::Serde(path.to_path_buf(), e))?;
 
 		if manifest.workspace_members.is_empty() {
 			return Ok(HashSet::new());
@@ -417,23 +425,33 @@ pub async fn find_roots(
 	while let Some(path) = current_path {
 		current_path = path.parent().map(Path::to_path_buf);
 
-		if !path.join(MANIFEST_FILE_NAME).exists() {
-			continue;
+		if workspace_dir.is_some() {
+			if let Some(project_root) = project_root {
+				return Ok((project_root, workspace_dir));
+			}
 		}
 
-		match (project_root.as_ref(), workspace_dir.as_ref()) {
-			(Some(project_root), Some(workspace_dir)) => {
-				return Ok((project_root.clone(), Some(workspace_dir.clone())));
-			}
+		let mut manifest = match fs::File::open(path.join(MANIFEST_FILE_NAME)).await {
+			Ok(manifest) => manifest,
+			Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
+			Err(e) => return Err(errors::ManifestReadError::Io(e).into()),
+		};
 
+		match (project_root.as_ref(), workspace_dir.as_ref()) {
 			(Some(project_root), None) => {
-				if get_workspace_members(&path).await?.contains(project_root) {
+				if get_workspace_members(&mut manifest, &path)
+					.await?
+					.contains(project_root)
+				{
 					workspace_dir = Some(path);
 				}
 			}
 
 			(None, None) => {
-				if get_workspace_members(&path).await?.contains(&cwd) {
+				if get_workspace_members(&mut manifest, &path)
+					.await?
+					.contains(&cwd)
+				{
 					// initializing a new member of a workspace
 					return Ok((cwd, Some(path)));
 				}
@@ -441,7 +459,7 @@ pub async fn find_roots(
 				project_root = Some(path);
 			}
 
-			(None, Some(_)) => unreachable!(),
+			(_, _) => unreachable!(),
 		}
 	}
 
diff --git a/src/patches.rs b/src/patches.rs
index 71f3d89..c2621a3 100644
--- a/src/patches.rs
+++ b/src/patches.rs
@@ -142,6 +142,7 @@ where
 	}
 
 	spawn_blocking(move || {
+		#[allow(clippy::disallowed_methods)]
 		let repo = if dot_git.exists() {
 			let repo = Repository::open(&container_folder)?;
 			reset_repo(&repo)?;