use anyhow::Result;
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
use crate::cache::Cache;
use crate::lockfile::LockFile;
use crate::manifest::Manifest;
pub struct InstallContext<'a> {
pub project_dir: &'a Path,
pub cache: &'a Cache,
pub force_refresh: bool,
pub verbose: bool,
pub manifest: Option<&'a Manifest>,
pub lockfile: Option<&'a Arc<LockFile>>,
pub old_lockfile: Option<&'a LockFile>,
pub project_patches: Option<&'a crate::manifest::ManifestPatches>,
pub private_patches: Option<&'a crate::manifest::ManifestPatches>,
pub gitignore_lock: Option<&'a Arc<Mutex<()>>>,
pub max_content_file_size: Option<u64>,
pub template_context_builder: Arc<crate::templating::TemplateContextBuilder>,
}
impl<'a> InstallContext<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
project_dir: &'a Path,
cache: &'a Cache,
force_refresh: bool,
verbose: bool,
manifest: Option<&'a Manifest>,
lockfile: Option<&'a Arc<LockFile>>,
old_lockfile: Option<&'a LockFile>,
project_patches: Option<&'a crate::manifest::ManifestPatches>,
private_patches: Option<&'a crate::manifest::ManifestPatches>,
gitignore_lock: Option<&'a Arc<Mutex<()>>>,
max_content_file_size: Option<u64>,
) -> Self {
let (lockfile_for_builder, project_config) = if let Some(lf) = lockfile {
(lf.clone(), manifest.and_then(|m| m.project.clone()))
} else {
(Arc::new(LockFile::default()), None)
};
let template_context_builder = Arc::new(crate::templating::TemplateContextBuilder::new(
lockfile_for_builder,
project_config,
Arc::new(cache.clone()),
project_dir.to_path_buf(),
));
Self {
project_dir,
cache,
force_refresh,
verbose,
manifest,
lockfile,
old_lockfile,
project_patches,
private_patches,
gitignore_lock,
max_content_file_size,
template_context_builder,
}
}
}
pub(crate) async fn read_with_cache_retry(path: &Path) -> Result<String> {
use std::io;
let retry_strategy = tokio_retry::strategy::ExponentialBackoff::from_millis(10)
.max_delay(Duration::from_millis(500))
.factor(2)
.take(10);
let path_buf = path.to_path_buf();
tokio_retry::Retry::spawn(retry_strategy, || {
let path = path_buf.clone();
async move {
tokio::fs::read_to_string(&path).await.map_err(|e| {
if e.kind() == io::ErrorKind::NotFound {
tracing::debug!(
"File not yet visible (likely cache coherency issue): {}",
path.display()
);
format!("File not found: {}", path.display())
} else {
format!("I/O error (non-retriable): {}", e)
}
})
}
})
.await
.map_err(|e| anyhow::anyhow!("Failed to read resource file: {}: {}", path.display(), e))
}