use crate::cli::Cli;
use crate::localization::{self, keys};
use crate::status::{PipelineStage, StatusReporter, report_pipeline_stage};
use anyhow::{Context, Result, anyhow};
use camino::{Utf8Path, Utf8PathBuf};
use cap_std::{ambient_authority, fs_utf8::Dir};
use std::borrow::Cow;
use std::io::{self, ErrorKind};
use std::path::Path;
use super::RunnerError;
pub(super) fn resolve_manifest_path(cli: &Cli) -> Result<Utf8PathBuf> {
let resolved = cli
.directory
.as_ref()
.map_or_else(|| cli.file.clone(), |dir| dir.join(&cli.file));
if resolved.file_name().is_none() {
return Err(anyhow!(
"{}",
localization::message(keys::RUNNER_MANIFEST_PATH_MISSING_NAME)
.with_arg("path", resolved.as_str())
));
}
Ok(resolved)
}
#[must_use]
pub(super) fn resolve_output_path<'a>(cli: &Cli, path: &'a Path) -> Cow<'a, Path> {
if path.is_relative() {
cli.directory.as_ref().map_or_else(
|| Cow::Borrowed(path),
|dir| Cow::Owned(dir.as_std_path().join(path)),
)
} else {
Cow::Borrowed(path)
}
}
pub(super) fn ensure_manifest_exists_or_error(
cli: &Cli,
reporter: &dyn StatusReporter,
manifest_path: &Utf8PathBuf,
) -> Result<()> {
let result = ensure_manifest_exists(cli, manifest_path);
if result
.as_ref()
.err()
.and_then(|error| error.downcast_ref::<RunnerError>())
.is_some_and(|error| matches!(error, RunnerError::ManifestNotFound { .. }))
{
report_pipeline_stage(reporter, PipelineStage::ManifestIngestion, None);
}
result
}
pub(super) fn ensure_manifest_exists(cli: &Cli, manifest_path: &Utf8PathBuf) -> Result<()> {
match manifest_metadata(manifest_path) {
Ok(()) => return Ok(()),
Err(error) if error.kind() != ErrorKind::NotFound => {
return Err(error)
.with_context(|| format!("inspect manifest metadata at {manifest_path}"));
}
Err(_) => {}
}
let manifest_name = manifest_path
.file_name()
.ok_or_else(|| {
anyhow!(
"{}",
localization::message(keys::RUNNER_MANIFEST_PATH_MISSING_NAME)
.with_arg("path", manifest_path.as_str())
)
})?
.to_owned();
let directory = if cli.directory.is_some() {
let parent = manifest_path
.parent()
.map_or_else(|| manifest_path.as_str(), camino::Utf8Path::as_str);
localization::message(keys::RUNNER_MANIFEST_DIRECTORY)
.with_arg("directory", parent)
.to_string()
} else {
localization::message(keys::RUNNER_MANIFEST_CURRENT_DIRECTORY).to_string()
};
let message = localization::message(keys::RUNNER_MANIFEST_NOT_FOUND)
.with_arg("manifest_name", manifest_name.as_str())
.with_arg("directory", &directory);
Err(RunnerError::ManifestNotFound {
manifest_name,
directory,
path: manifest_path.to_path_buf().into_std_path_buf(),
message,
help: localization::message(keys::RUNNER_MANIFEST_NOT_FOUND_HELP),
}
.into())
}
fn manifest_metadata(manifest_path: &Utf8Path) -> io::Result<()> {
let parent = manifest_path
.parent()
.filter(|path| !path.as_str().is_empty())
.unwrap_or_else(|| Utf8Path::new("."));
let directory = Dir::open_ambient_dir(parent, ambient_authority())?;
let name = manifest_path.file_name().ok_or_else(|| {
io::Error::new(
ErrorKind::InvalidInput,
format!("manifest path {manifest_path} has no file name"),
)
})?;
directory.metadata(Utf8Path::new(name))?;
Ok(())
}