mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-05-05 19:50:02 +00:00
Rename lib to binstalk (#361)
This commit is contained in:
parent
a94d83f0d5
commit
e25aa50ec9
49 changed files with 25 additions and 25 deletions
58
crates/binstalk/src/drivers/crates_io.rs
Normal file
58
crates/binstalk/src/drivers/crates_io.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use cargo_toml::Manifest;
|
||||
use crates_io_api::AsyncClient;
|
||||
use log::debug;
|
||||
use reqwest::Client;
|
||||
use semver::VersionReq;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
errors::BinstallError,
|
||||
helpers::download::Download,
|
||||
manifests::cargo_toml_binstall::{Meta, TarBasedFmt},
|
||||
};
|
||||
|
||||
use super::find_version;
|
||||
|
||||
mod vfs;
|
||||
|
||||
mod visitor;
|
||||
use visitor::ManifestVisitor;
|
||||
|
||||
/// Fetch a crate Cargo.toml by name and version from crates.io
|
||||
pub async fn fetch_crate_cratesio(
|
||||
client: Client,
|
||||
crates_io_api_client: &AsyncClient,
|
||||
name: &str,
|
||||
version_req: &VersionReq,
|
||||
) -> Result<Manifest<Meta>, BinstallError> {
|
||||
// Fetch / update index
|
||||
debug!("Looking up crate information");
|
||||
|
||||
// Fetch online crate information
|
||||
let base_info = crates_io_api_client
|
||||
.get_crate(name.as_ref())
|
||||
.await
|
||||
.map_err(|err| BinstallError::CratesIoApi {
|
||||
crate_name: name.into(),
|
||||
err,
|
||||
})?;
|
||||
|
||||
// Locate matching version
|
||||
let version_iter = base_info.versions.iter().filter(|v| !v.yanked);
|
||||
let (version, version_name) = find_version(version_req, version_iter)?;
|
||||
|
||||
debug!("Found information for crate version: '{}'", version.num);
|
||||
|
||||
// Download crate to temporary dir (crates.io or git?)
|
||||
let crate_url = format!("https://crates.io/{}", version.dl_path);
|
||||
|
||||
debug!("Fetching crate from: {crate_url} and extracting Cargo.toml from it");
|
||||
|
||||
let manifest_dir_path: PathBuf = format!("{name}-{version_name}").into();
|
||||
|
||||
Download::new(client, Url::parse(&crate_url)?)
|
||||
.and_visit_tar(TarBasedFmt::Tgz, ManifestVisitor::new(manifest_dir_path))
|
||||
.await
|
||||
}
|
49
crates/binstalk/src/drivers/crates_io/vfs.rs
Normal file
49
crates/binstalk/src/drivers/crates_io/vfs.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use std::{
|
||||
collections::{hash_set::HashSet, BTreeMap},
|
||||
io,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use cargo_toml::AbstractFilesystem;
|
||||
use normalize_path::NormalizePath;
|
||||
|
||||
/// This type stores the filesystem structure for the crate tarball
|
||||
/// extracted in memory and can be passed to
|
||||
/// `cargo_toml::Manifest::complete_from_abstract_filesystem`.
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct Vfs(BTreeMap<Box<Path>, HashSet<Box<str>>>);
|
||||
|
||||
impl Vfs {
|
||||
/// * `path` - must be canonical, must not be empty.
|
||||
pub(super) fn add_path(&mut self, mut path: &Path) {
|
||||
while let Some(parent) = path.parent() {
|
||||
// Since path has parent, it must have a filename
|
||||
let filename = path.file_name().unwrap();
|
||||
|
||||
// `cargo_toml`'s implementation does the same thing.
|
||||
// https://docs.rs/cargo_toml/0.11.5/src/cargo_toml/afs.rs.html#24
|
||||
let filename = filename.to_string_lossy();
|
||||
|
||||
self.0
|
||||
.entry(parent.into())
|
||||
.or_insert_with(|| HashSet::with_capacity(4))
|
||||
.insert(filename.into());
|
||||
|
||||
path = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractFilesystem for Vfs {
|
||||
fn file_names_in(&self, rel_path: &str) -> io::Result<HashSet<Box<str>>> {
|
||||
let rel_path = Path::new(rel_path).normalize();
|
||||
|
||||
Ok(self.0.get(&*rel_path).map(Clone::clone).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractFilesystem for &Vfs {
|
||||
fn file_names_in(&self, rel_path: &str) -> io::Result<HashSet<Box<str>>> {
|
||||
(*self).file_names_in(rel_path)
|
||||
}
|
||||
}
|
86
crates/binstalk/src/drivers/crates_io/visitor.rs
Normal file
86
crates/binstalk/src/drivers/crates_io/visitor.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use std::{
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use cargo_toml::Manifest;
|
||||
use log::debug;
|
||||
use normalize_path::NormalizePath;
|
||||
use tar::Entries;
|
||||
|
||||
use super::vfs::Vfs;
|
||||
use crate::{
|
||||
errors::BinstallError, helpers::download::TarEntriesVisitor,
|
||||
manifests::cargo_toml_binstall::Meta,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ManifestVisitor {
|
||||
cargo_toml_content: Vec<u8>,
|
||||
/// manifest_dir_path is treated as the current dir.
|
||||
manifest_dir_path: PathBuf,
|
||||
|
||||
vfs: Vfs,
|
||||
}
|
||||
|
||||
impl ManifestVisitor {
|
||||
pub(super) fn new(manifest_dir_path: PathBuf) -> Self {
|
||||
Self {
|
||||
// Cargo.toml is quite large usually.
|
||||
cargo_toml_content: Vec::with_capacity(2000),
|
||||
manifest_dir_path,
|
||||
vfs: Vfs::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TarEntriesVisitor for ManifestVisitor {
|
||||
type Target = Manifest<Meta>;
|
||||
|
||||
fn visit<R: Read>(&mut self, entries: Entries<'_, R>) -> Result<(), BinstallError> {
|
||||
for res in entries {
|
||||
let mut entry = res?;
|
||||
let path = entry.path()?;
|
||||
let path = path.normalize();
|
||||
|
||||
let path = if let Ok(path) = path.strip_prefix(&self.manifest_dir_path) {
|
||||
path
|
||||
} else {
|
||||
// The path is outside of the curr dir (manifest dir),
|
||||
// ignore it.
|
||||
continue;
|
||||
};
|
||||
|
||||
if path == Path::new("Cargo.toml")
|
||||
|| path == Path::new("src/main.rs")
|
||||
|| path.starts_with("src/bin")
|
||||
{
|
||||
self.vfs.add_path(path);
|
||||
}
|
||||
|
||||
if path == Path::new("Cargo.toml") {
|
||||
// Since it is possible for the same Cargo.toml to appear
|
||||
// multiple times using `tar --keep-old-files`, here we
|
||||
// clear the buffer first before reading into it.
|
||||
self.cargo_toml_content.clear();
|
||||
entry.read_to_end(&mut self.cargo_toml_content)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load binstall metadata using the extracted information stored in memory.
|
||||
fn finish(self) -> Result<Self::Target, BinstallError> {
|
||||
debug!("Loading manifest directly from extracted file");
|
||||
|
||||
// Load and parse manifest
|
||||
let mut manifest = Manifest::from_slice_with_metadata(&self.cargo_toml_content)?;
|
||||
|
||||
// Checks vfs for binary output names
|
||||
manifest.complete_from_abstract_filesystem(&self.vfs)?;
|
||||
|
||||
// Return metadata
|
||||
Ok(manifest)
|
||||
}
|
||||
}
|
52
crates/binstalk/src/drivers/version.rs
Normal file
52
crates/binstalk/src/drivers/version.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use log::debug;
|
||||
use semver::VersionReq;
|
||||
|
||||
use crate::errors::BinstallError;
|
||||
|
||||
pub(super) trait Version {
|
||||
/// Return `None` on error.
|
||||
fn get_version(&self) -> Option<semver::Version>;
|
||||
}
|
||||
|
||||
impl<T: Version> Version for &T {
|
||||
fn get_version(&self) -> Option<semver::Version> {
|
||||
(*self).get_version()
|
||||
}
|
||||
}
|
||||
|
||||
impl Version for crates_io_api::Version {
|
||||
fn get_version(&self) -> Option<semver::Version> {
|
||||
// Remove leading `v` for git tags
|
||||
let ver_str = match self.num.strip_prefix('v') {
|
||||
Some(v) => v,
|
||||
None => &self.num,
|
||||
};
|
||||
|
||||
// Parse out version
|
||||
semver::Version::parse(ver_str).ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn find_version<Item: Version, VersionIter: Iterator<Item = Item>>(
|
||||
version_req: &VersionReq,
|
||||
version_iter: VersionIter,
|
||||
) -> Result<(Item, semver::Version), BinstallError> {
|
||||
version_iter
|
||||
// Filter for matching versions
|
||||
.filter_map(|item| {
|
||||
let ver = item.get_version()?;
|
||||
|
||||
// Filter by version match
|
||||
if version_req.matches(&ver) {
|
||||
debug!("Version: {:?}", ver);
|
||||
Some((item, ver))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
// Return highest version
|
||||
.max_by_key(|(_item, ver)| ver.clone())
|
||||
.ok_or(BinstallError::VersionMismatch {
|
||||
req: version_req.clone(),
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue