use std::{ io::{self, Read}, path::{Path, PathBuf}, }; use cargo_toml::Manifest; use normalize_path::NormalizePath; use tracing::debug; use super::vfs::Vfs; use crate::{ errors::BinstallError, helpers::download::{DownloadError, Entries, TarEntriesVisitor}, manifests::cargo_toml_binstall::Meta, }; #[derive(Debug)] pub(super) struct ManifestVisitor { cargo_toml_content: Vec, /// 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; fn visit(&mut self, entries: Entries<'_, R>) -> Result<(), DownloadError> { for res in entries { let mut entry = res?; let path = entry.path()?; let path = path.normalize(); let Ok(path) = path.strip_prefix(&self.manifest_dir_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 { Ok(load_manifest(&self.cargo_toml_content, &self.vfs).map_err(io::Error::from)?) } } fn load_manifest(slice: &[u8], vfs: &Vfs) -> Result, BinstallError> { debug!("Loading manifest directly from extracted file"); // Load and parse manifest let mut manifest = Manifest::from_slice_with_metadata(slice)?; // Checks vfs for binary output names manifest.complete_from_abstract_filesystem(vfs)?; // Return metadata Ok(manifest) }