use super::RunError;
use dpm_model::{
Artifact, ArtifactKind, AssetId, AssetKind, ExactSource, Plan, RunSource, WorkItem,
is_exact_commit,
};
pub(super) fn resolve(
plan: &Plan,
work: &WorkItem,
source: &RunSource,
) -> Result<ExactSource, RunError> {
match source {
RunSource::GitCommit { asset, commit } => {
repository(plan, work, *asset)?;
Ok(ExactSource {
asset: *asset,
commit: commit.clone(),
artifact: None,
})
}
RunSource::Artifact { artifact } => {
let found = plan
.artifacts
.get(artifact)
.ok_or_else(|| unsupported(format!("artifact {artifact} is not in the plan")))?;
if !work.execution.artifact_ids.contains(artifact) {
return Err(unsupported(format!(
"artifact {artifact} is not attached to {}",
work.key
)));
}
let (asset, commit) = evidence(artifact.to_string(), found)?;
repository(plan, work, asset)?;
Ok(ExactSource {
asset,
commit,
artifact: Some(*artifact),
})
}
}
}
fn evidence(label: String, found: &Artifact) -> Result<(AssetId, String), RunError> {
if found.kind != ArtifactKind::GitCommit {
return Err(unsupported(format!(
"artifact {label} is not Git commit evidence"
)));
}
let metadata = |name: &str| {
found
.metadata
.get(name)
.ok_or_else(|| unsupported(format!("artifact {label} has no `{name}` metadata")))
};
let commit = metadata("commit")?;
if !is_exact_commit(commit) {
return Err(unsupported(format!(
"artifact {label} names {commit:?}, not a full commit; a branch or abbreviation is not an exact source"
)));
}
let asset: AssetId = metadata("asset_id")?
.parse()
.map_err(|_| unsupported(format!("artifact {label} has an invalid `asset_id`")))?;
if found.uri != format!("git:asset:{asset}@{commit}") {
return Err(unsupported(format!(
"artifact {label} locator {:?} disagrees with its commit and asset",
found.uri
)));
}
Ok((asset, commit.clone()))
}
fn repository(plan: &Plan, work: &WorkItem, asset: AssetId) -> Result<(), RunError> {
if !work.contract.assets.iter().any(|need| need.asset == asset) {
return Err(unsupported(format!(
"asset {asset} is not required by {}",
work.key
)));
}
match plan.assets.get(&asset).map(|found| &found.kind) {
Some(AssetKind::GitRepository { .. }) => Ok(()),
Some(_) => Err(unsupported(format!(
"asset {asset} is not a Git repository"
))),
None => Err(unsupported(format!("asset {asset} is not in the plan"))),
}
}
fn unsupported(reason: String) -> RunError {
RunError::UnsupportedSource(reason)
}