mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-24 22:30:03 +00:00
Refactor: Extract cargo_toml_workspace
as a new crate (#1287)
To reduce codegen time of `binstalk` and also enable others to reuse this crate. Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
c57356e870
commit
8ff13c1b36
19 changed files with 290 additions and 47 deletions
|
@ -14,12 +14,11 @@ async-trait = "0.1.68"
|
|||
base16 = "0.2.1"
|
||||
binstalk-downloader = { version = "0.7.0", path = "../binstalk-downloader", default-features = false, features = ["gh-api-client"] }
|
||||
binstalk-types = { version = "0.5.0", path = "../binstalk-types" }
|
||||
cargo_toml = "0.15.3"
|
||||
cargo-toml-workspace = { version = "0.0.0", path = "../cargo-toml-workspace" }
|
||||
command-group = { version = "2.1.0", features = ["with-tokio"] }
|
||||
compact_str = { version = "0.7.0", features = ["serde"] }
|
||||
detect-targets = { version = "0.1.10", path = "../detect-targets" }
|
||||
either = "1.8.1"
|
||||
glob = "0.3.1"
|
||||
home = "0.5.5"
|
||||
itertools = "0.11.0"
|
||||
jobslot = { version = "0.2.11", features = ["tokio"] }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
use base16::DecodeError as Base16DecodeError;
|
||||
use cargo_toml::Manifest;
|
||||
use compact_str::CompactString;
|
||||
use leon::{ParseError, RenderError};
|
||||
use miette::Diagnostic;
|
||||
|
@ -11,7 +10,10 @@ use thiserror::Error as ThisError;
|
|||
|
||||
use crate::{
|
||||
errors::BinstallError,
|
||||
helpers::remote::{Client, Error as RemoteError, Url, UrlParseError},
|
||||
helpers::{
|
||||
cargo_toml::Manifest,
|
||||
remote::{Client, Error as RemoteError, Url, UrlParseError},
|
||||
},
|
||||
manifests::cargo_toml_binstall::Meta,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use base16::{decode as decode_base16, encode_lower as encode_base16};
|
||||
use cargo_toml::Manifest;
|
||||
use compact_str::{format_compact, CompactString, ToCompactString};
|
||||
use leon::{Template, Values};
|
||||
use semver::{Version, VersionReq};
|
||||
|
@ -15,6 +14,7 @@ use crate::{
|
|||
errors::BinstallError,
|
||||
helpers::{
|
||||
bytes::Bytes,
|
||||
cargo_toml::Manifest,
|
||||
download::{DataVerifier, Download},
|
||||
remote::{Client, Url},
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use binstalk_downloader::remote::Error as RemoteError;
|
||||
use cargo_toml::Manifest;
|
||||
use compact_str::{CompactString, ToCompactString};
|
||||
use semver::{Comparator, Op as ComparatorOp, Version as SemVersion, VersionReq};
|
||||
use serde::Deserialize;
|
||||
|
@ -12,7 +11,10 @@ use tracing::debug;
|
|||
use crate::{
|
||||
drivers::registry::{parse_manifest, MatchedVersion, RegistryError},
|
||||
errors::BinstallError,
|
||||
helpers::remote::{Client, Url},
|
||||
helpers::{
|
||||
cargo_toml::Manifest,
|
||||
remote::{Client, Url},
|
||||
},
|
||||
manifests::cargo_toml_binstall::Meta,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::{io, path::PathBuf, sync::Arc};
|
||||
|
||||
use cargo_toml::Manifest;
|
||||
use compact_str::{CompactString, ToCompactString};
|
||||
use once_cell::sync::OnceCell;
|
||||
use semver::VersionReq;
|
||||
|
@ -16,6 +15,7 @@ use crate::{
|
|||
},
|
||||
errors::BinstallError,
|
||||
helpers::{
|
||||
cargo_toml::Manifest,
|
||||
git::{GitUrl, Repository},
|
||||
remote::Client,
|
||||
},
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use cargo_toml::Manifest;
|
||||
use compact_str::CompactString;
|
||||
use semver::VersionReq;
|
||||
use serde_json::Deserializer as JsonDeserializer;
|
||||
|
@ -11,7 +10,10 @@ use crate::{
|
|||
RegistryConfig, RegistryError,
|
||||
},
|
||||
errors::BinstallError,
|
||||
helpers::remote::{Client, Error as RemoteError},
|
||||
helpers::{
|
||||
cargo_toml::Manifest,
|
||||
remote::{Client, Error as RemoteError},
|
||||
},
|
||||
manifests::cargo_toml_binstall::Meta,
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
path::Path,
|
||||
};
|
||||
|
||||
use cargo_toml::AbstractFilesystem;
|
||||
use crate::helpers::cargo_toml::AbstractFilesystem;
|
||||
use normalize_path::NormalizePath;
|
||||
|
||||
/// This type stores the filesystem structure for the crate tarball
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use cargo_toml::{Manifest, Value};
|
||||
use normalize_path::NormalizePath;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tracing::debug;
|
||||
|
@ -8,7 +7,10 @@ use tracing::debug;
|
|||
use super::vfs::Vfs;
|
||||
use crate::{
|
||||
errors::BinstallError,
|
||||
helpers::download::{DownloadError, TarEntriesVisitor, TarEntry},
|
||||
helpers::{
|
||||
cargo_toml::{Manifest, Value},
|
||||
download::{DownloadError, TarEntriesVisitor, TarEntry},
|
||||
},
|
||||
manifests::cargo_toml_binstall::Meta,
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ use std::{
|
|||
use binstalk_downloader::{
|
||||
download::DownloadError, gh_api_client::GhApiError, remote::Error as RemoteError,
|
||||
};
|
||||
use cargo_toml::Error as CargoTomlError;
|
||||
use compact_str::CompactString;
|
||||
use miette::{Diagnostic, Report};
|
||||
use target_lexicon::ParseError as TargetTripleParseError;
|
||||
|
@ -17,7 +16,9 @@ use tracing::{error, warn};
|
|||
|
||||
use crate::{
|
||||
drivers::{InvalidRegistryError, RegistryError},
|
||||
helpers::cargo_toml_workspace::LoadManifestFromWSError,
|
||||
helpers::{
|
||||
cargo_toml::Error as CargoTomlError, cargo_toml_workspace::Error as LoadManifestFromWSError,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -500,3 +501,9 @@ impl From<InvalidRegistryError> for BinstallError {
|
|||
BinstallError::RegistryParseError(Box::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadManifestFromWSError> for BinstallError {
|
||||
fn from(e: LoadManifestFromWSError) -> Self {
|
||||
BinstallError::LoadManifestFromWSError(Box::new(e))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pub(crate) mod cargo_toml_workspace;
|
||||
pub(crate) mod futures_resolver;
|
||||
pub mod jobserver_client;
|
||||
pub mod remote;
|
||||
|
@ -11,6 +10,7 @@ pub(crate) use binstalk_downloader::{bytes, download};
|
|||
|
||||
#[cfg(feature = "git")]
|
||||
pub(crate) use binstalk_downloader::git;
|
||||
pub(crate) use cargo_toml_workspace::{self, cargo_toml};
|
||||
|
||||
pub(crate) fn is_universal_macos(target: &str) -> bool {
|
||||
["universal-apple-darwin", "universal2-apple-darwin"].contains(&target)
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
use std::{
|
||||
io, mem,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use cargo_toml::{Error as CargoTomlError, Manifest};
|
||||
use compact_str::CompactString;
|
||||
use glob::PatternError;
|
||||
use normalize_path::NormalizePath;
|
||||
use thiserror::Error as ThisError;
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
use crate::{errors::BinstallError, manifests::cargo_toml_binstall::Meta};
|
||||
|
||||
/// Load binstall metadata `Cargo.toml` from workspace at the provided path
|
||||
///
|
||||
/// WARNING: This is a blocking operation.
|
||||
///
|
||||
/// * `workspace_path` - can be a directory (path to workspace) or
|
||||
/// a file (path to `Cargo.toml`).
|
||||
pub fn load_manifest_from_workspace(
|
||||
workspace_path: impl AsRef<Path>,
|
||||
crate_name: impl AsRef<str>,
|
||||
) -> Result<Manifest<Meta>, BinstallError> {
|
||||
fn inner(workspace_path: &Path, crate_name: &str) -> Result<Manifest<Meta>, BinstallError> {
|
||||
load_manifest_from_workspace_inner(workspace_path, crate_name).map_err(|inner| {
|
||||
Box::new(LoadManifestFromWSError {
|
||||
workspace_path: workspace_path.into(),
|
||||
crate_name: crate_name.into(),
|
||||
inner,
|
||||
})
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
inner(workspace_path.as_ref(), crate_name.as_ref())
|
||||
}
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
#[error("Failed to load {crate_name} from {}: {inner}", workspace_path.display())]
|
||||
pub struct LoadManifestFromWSError {
|
||||
workspace_path: Box<Path>,
|
||||
crate_name: CompactString,
|
||||
#[source]
|
||||
inner: LoadManifestFromWSErrorInner,
|
||||
}
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
enum LoadManifestFromWSErrorInner {
|
||||
#[error("Invalid pattern in workspace.members or workspace.exclude: {0}")]
|
||||
PatternError(#[from] PatternError),
|
||||
|
||||
#[error("Invalid pattern `{0}`: It must be relative and point within current dir")]
|
||||
InvalidPatternError(CompactString),
|
||||
|
||||
#[error("Failed to parse cargo manifest: {0}")]
|
||||
CargoManifest(#[from] CargoTomlError),
|
||||
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
#[error("Not found")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
fn load_manifest_from_workspace_inner(
|
||||
workspace_path: &Path,
|
||||
crate_name: &str,
|
||||
) -> Result<Manifest<Meta>, LoadManifestFromWSErrorInner> {
|
||||
debug!(
|
||||
"Loading manifest of crate {crate_name} from workspace: {}",
|
||||
workspace_path.display()
|
||||
);
|
||||
|
||||
let manifest_path = if workspace_path.is_file() {
|
||||
workspace_path.to_owned()
|
||||
} else {
|
||||
workspace_path.join("Cargo.toml")
|
||||
};
|
||||
|
||||
let mut manifest_paths = vec![manifest_path];
|
||||
|
||||
while let Some(manifest_path) = manifest_paths.pop() {
|
||||
let manifest = Manifest::<Meta>::from_path_with_metadata(&manifest_path)?;
|
||||
|
||||
let name = manifest.package.as_ref().map(|p| &*p.name);
|
||||
debug!(
|
||||
"Loading from {}, manifest.package.name = {:#?}",
|
||||
manifest_path.display(),
|
||||
name
|
||||
);
|
||||
|
||||
if name == Some(crate_name) {
|
||||
return Ok(manifest);
|
||||
}
|
||||
|
||||
if let Some(ws) = manifest.workspace {
|
||||
let excludes = ws.exclude;
|
||||
let members = ws.members;
|
||||
|
||||
if members.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let exclude_patterns = excludes
|
||||
.into_iter()
|
||||
.map(|pat| Pattern::new(&pat))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let workspace_path = manifest_path.parent().unwrap();
|
||||
|
||||
for member in members {
|
||||
for path in Pattern::new(&member)?.glob_dirs(workspace_path)? {
|
||||
if !exclude_patterns
|
||||
.iter()
|
||||
.any(|exclude| exclude.matches_with_trailing(&path))
|
||||
{
|
||||
manifest_paths.push(workspace_path.join(path).join("Cargo.toml"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(LoadManifestFromWSErrorInner::NotFound)
|
||||
}
|
||||
|
||||
struct Pattern(Vec<glob::Pattern>);
|
||||
|
||||
impl Pattern {
|
||||
fn new(pat: &str) -> Result<Self, LoadManifestFromWSErrorInner> {
|
||||
Path::new(pat)
|
||||
.try_normalize()
|
||||
.ok_or_else(|| LoadManifestFromWSErrorInner::InvalidPatternError(pat.into()))?
|
||||
.iter()
|
||||
.map(|c| glob::Pattern::new(c.to_str().unwrap()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(Into::into)
|
||||
.map(Self)
|
||||
}
|
||||
|
||||
/// * `glob_path` - path to dir to glob for
|
||||
/// return paths relative to `glob_path`.
|
||||
fn glob_dirs(&self, glob_path: &Path) -> Result<Vec<PathBuf>, LoadManifestFromWSErrorInner> {
|
||||
let mut paths = vec![PathBuf::new()];
|
||||
|
||||
for pattern in &self.0 {
|
||||
if paths.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
for path in mem::take(&mut paths) {
|
||||
let p = glob_path.join(&path);
|
||||
let res = p.read_dir();
|
||||
if res.is_err() && !p.is_dir() {
|
||||
continue;
|
||||
}
|
||||
drop(p);
|
||||
|
||||
for res in res? {
|
||||
let entry = res?;
|
||||
|
||||
let is_dir = entry
|
||||
.file_type()
|
||||
.map(|file_type| file_type.is_dir() || file_type.is_symlink())
|
||||
.unwrap_or(false);
|
||||
if !is_dir {
|
||||
continue;
|
||||
}
|
||||
|
||||
let filename = entry.file_name();
|
||||
if filename != "." // Ignore current dir
|
||||
&& filename != ".." // Ignore parent dir
|
||||
&& pattern.matches(&filename.to_string_lossy())
|
||||
{
|
||||
paths.push(path.join(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(paths)
|
||||
}
|
||||
|
||||
/// Return `true` if `path` matches the pattern.
|
||||
/// It will still return `true` even if there are some trailing components.
|
||||
fn matches_with_trailing(&self, path: &Path) -> bool {
|
||||
let mut iter = path.iter().map(|os_str| os_str.to_string_lossy());
|
||||
for pattern in &self.0 {
|
||||
match iter.next() {
|
||||
Some(s) if pattern.matches(&s) => (),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs::create_dir_all as mkdir;
|
||||
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_glob_dirs() {
|
||||
let pattern = Pattern::new("*/*/q/*").unwrap();
|
||||
let tempdir = TempDir::new().unwrap();
|
||||
|
||||
mkdir(tempdir.as_ref().join("a/b/c/efe")).unwrap();
|
||||
mkdir(tempdir.as_ref().join("a/b/q/ww")).unwrap();
|
||||
mkdir(tempdir.as_ref().join("d/233/q/d")).unwrap();
|
||||
|
||||
let mut paths = pattern.glob_dirs(tempdir.as_ref()).unwrap();
|
||||
paths.sort_unstable();
|
||||
assert_eq!(
|
||||
paths,
|
||||
vec![PathBuf::from("a/b/q/ww"), PathBuf::from("d/233/q/d")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matches_with_trailing() {
|
||||
let pattern = Pattern::new("*/*/q/*").unwrap();
|
||||
|
||||
assert!(pattern.matches_with_trailing(Path::new("a/b/q/d/")));
|
||||
assert!(pattern.matches_with_trailing(Path::new("a/b/q/d")));
|
||||
assert!(pattern.matches_with_trailing(Path::new("a/b/q/d/234")));
|
||||
assert!(pattern.matches_with_trailing(Path::new("a/234/q/d/234")));
|
||||
|
||||
assert!(!pattern.matches_with_trailing(Path::new("")));
|
||||
assert!(!pattern.matches_with_trailing(Path::new("a/")));
|
||||
assert!(!pattern.matches_with_trailing(Path::new("a/234")));
|
||||
assert!(!pattern.matches_with_trailing(Path::new("a/234/q")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load() {
|
||||
let p = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("e2e-tests/manifests/workspace");
|
||||
|
||||
let manifest = load_manifest_from_workspace(&p, "cargo-binstall").unwrap();
|
||||
let package = manifest.package.unwrap();
|
||||
assert_eq!(package.name, "cargo-binstall");
|
||||
assert_eq!(package.version.as_ref().unwrap(), "0.12.0");
|
||||
assert_eq!(manifest.bin.len(), 1);
|
||||
assert_eq!(manifest.bin[0].name.as_deref().unwrap(), "cargo-binstall");
|
||||
assert_eq!(manifest.bin[0].path.as_deref().unwrap(), "src/main.rs");
|
||||
|
||||
let err = load_manifest_from_workspace_inner(&p, "cargo-binstall2").unwrap_err();
|
||||
assert!(
|
||||
matches!(err, LoadManifestFromWSErrorInner::NotFound),
|
||||
"{:#?}",
|
||||
err
|
||||
);
|
||||
|
||||
let manifest = load_manifest_from_workspace(&p, "cargo-watch").unwrap();
|
||||
let package = manifest.package.unwrap();
|
||||
assert_eq!(package.name, "cargo-watch");
|
||||
assert_eq!(package.version.as_ref().unwrap(), "8.4.0");
|
||||
assert_eq!(manifest.bin.len(), 1);
|
||||
assert_eq!(manifest.bin[0].name.as_deref().unwrap(), "cargo-watch");
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use cargo_toml::Manifest;
|
||||
use compact_str::{CompactString, ToCompactString};
|
||||
use itertools::Itertools;
|
||||
use leon::Template;
|
||||
|
@ -22,8 +21,8 @@ use crate::{
|
|||
errors::{BinstallError, VersionParseError},
|
||||
fetchers::{Data, Fetcher, TargetData},
|
||||
helpers::{
|
||||
self, cargo_toml_workspace::load_manifest_from_workspace, download::ExtractedFiles,
|
||||
remote::Client, target_triple::TargetTriple,
|
||||
self, cargo_toml::Manifest, cargo_toml_workspace::load_manifest_from_workspace,
|
||||
download::ExtractedFiles, remote::Client, target_triple::TargetTriple,
|
||||
},
|
||||
manifests::cargo_toml_binstall::{Meta, PkgMeta, PkgOverride},
|
||||
ops::{CargoTomlFetchOverride, Options},
|
||||
|
@ -381,7 +380,7 @@ impl PackageInfo {
|
|||
let dir = TempDir::new()?;
|
||||
helpers::git::Repository::shallow_clone(git_url, dir.as_ref())?;
|
||||
|
||||
load_manifest_from_workspace(dir.as_ref(), &name)
|
||||
load_manifest_from_workspace(dir.as_ref(), &name).map_err(BinstallError::from)
|
||||
})
|
||||
.await??
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use binstalk::ops::resolve::load_manifest_path;
|
||||
use cargo_toml::Product;
|
||||
use cargo_toml_workspace::cargo_toml::{Edition, Product};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
|
@ -24,7 +24,7 @@ fn parse_meta() {
|
|||
&[Product {
|
||||
name: Some("cargo-binstall".to_string()),
|
||||
path: Some("src/main.rs".to_string()),
|
||||
edition: cargo_toml::Edition::E2021,
|
||||
edition: Edition::E2021,
|
||||
..Default::default()
|
||||
},],
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue