use crate::sources::registry::{LoadResponse, MaybeLock, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
use crate::util::{Filesystem, GlobalContext};
use crate::workspace::PackageId;
use cargo_util::{Sha256, paths};
use std::cell::Cell;
use std::fs::File;
use std::io::SeekFrom;
use std::io::{self, prelude::*};
use std::path::Path;
pub struct LocalRegistry<'gctx> {
index_path: Filesystem,
root: Filesystem,
src_path: Filesystem,
gctx: &'gctx GlobalContext,
updated: Cell<bool>,
quiet: bool,
}
impl<'gctx> LocalRegistry<'gctx> {
pub fn new(root: &Path, gctx: &'gctx GlobalContext, name: &str) -> LocalRegistry<'gctx> {
LocalRegistry {
src_path: gctx.registry_source_path().join(name),
index_path: Filesystem::new(root.join("index")),
root: Filesystem::new(root.to_path_buf()),
gctx,
updated: Cell::new(false),
quiet: false,
}
}
fn update(&self) -> CargoResult<()> {
if self.updated.get() {
return Ok(());
}
let root = self.root.clone().into_path_unlocked();
if !root.is_dir() {
anyhow::bail!("local registry path is not a directory: {}", root.display());
}
let index_path = self.index_path.clone().into_path_unlocked();
if !index_path.is_dir() {
anyhow::bail!(
"local registry index path is not a directory: {}",
index_path.display()
);
}
self.updated.set(true);
Ok(())
}
}
#[async_trait::async_trait(?Send)]
impl<'gctx> RegistryData for LocalRegistry<'gctx> {
fn prepare(&self) -> CargoResult<()> {
Ok(())
}
fn index_path(&self) -> &Filesystem {
&self.index_path
}
fn cache_path(&self) -> &Filesystem {
&self.root
}
fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
path.as_path_unlocked()
}
async fn load(
&self,
root: &Path,
path: &Path,
_index_version: Option<&str>,
) -> CargoResult<LoadResponse> {
if !self.updated.get() {
self.update()?;
}
let raw_data = match paths::read_bytes(&root.join(path)) {
Err(e)
if e.downcast_ref::<io::Error>()
.map_or(false, |ioe| ioe.kind() == io::ErrorKind::NotFound) =>
{
return Ok(LoadResponse::NotFound);
}
r => r,
}?;
Ok(LoadResponse::Data {
raw_data,
index_version: None,
})
}
async fn config(&self) -> CargoResult<Option<RegistryConfig>> {
Ok(None)
}
fn invalidate_cache(&self) {
}
fn set_quiet(&mut self, _quiet: bool) {
self.quiet = true;
}
fn is_updated(&self) -> bool {
true
}
async fn download(&self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
let path = self.root.join(&pkg.tarball_name()).into_path_unlocked();
let mut crate_file = paths::open(&path)?;
let dst = path.file_stem().unwrap();
if self.src_path.join(dst).into_path_unlocked().exists() {
return Ok(MaybeLock::Ready(crate_file));
}
if !self.quiet {
self.gctx.shell().status("Unpacking", pkg)?;
}
let actual = Sha256::new().update_file(&crate_file)?.finish_hex();
if actual != checksum {
anyhow::bail!("failed to verify the checksum of `{}`", pkg)
}
crate_file.seek(SeekFrom::Start(0))?;
Ok(MaybeLock::Ready(crate_file))
}
async fn finish_download(
&self,
_pkg: PackageId,
_checksum: &str,
_data: &[u8],
) -> CargoResult<File> {
panic!("this source doesn't download")
}
}