use color_eyre::eyre::{Report, eyre};
use postgresql_embedded::Version;
use super::{
Manifest,
ManifestArtifact,
ManifestExtension,
ManifestSource,
artifact_version,
unavailable,
};
use crate::{error::BootstrapResult, extensions::ExtensionName};
#[derive(Debug, Clone, Copy)]
pub struct ArtifactQuery<'a> {
pub name: &'a ExtensionName,
pub running: &'a Version,
pub target: &'a str,
}
#[derive(Debug, Clone, Copy)]
pub struct Selection<'a> {
pub extension: &'a ManifestExtension,
pub artifact: &'a ManifestArtifact,
}
impl Manifest {
pub fn select<'a>(
&'a self,
query: ArtifactQuery<'_>,
source: &ManifestSource,
) -> BootstrapResult<Selection<'a>> {
let ArtifactQuery {
name,
running,
target,
} = query;
let Some(extension) = self
.extensions
.iter()
.find(|extension| extension.name == name.as_str())
else {
return Err(unavailable(eyre!(
"manifest at {} lists no extension named {name}; it offers: {}",
source.location(),
self.extension_names().join(", ")
)));
};
let artifact = extension
.artifacts
.iter()
.find(|artifact| artifact_matches(artifact, running, target))
.ok_or_else(|| unavailable(no_artifact_report(extension, running, target, source)))?;
Ok(Selection {
extension,
artifact,
})
}
}
fn artifact_matches(artifact: &ManifestArtifact, running: &Version, target: &str) -> bool {
artifact_version(artifact).is_some_and(|built| {
built.major == running.major && built.minor == running.minor && artifact.target == target
})
}
fn no_artifact_report(
extension: &ManifestExtension,
running: &Version,
target: &str,
source: &ManifestSource,
) -> Report {
let offered: Vec<String> = extension
.artifacts
.iter()
.map(|artifact| format!("{} on {}", artifact.postgresql, artifact.target))
.collect();
eyre!(
"manifest at {} has no {} archive for PostgreSQL {}.{} on {target}; it offers: {}",
source.location(),
extension.name,
running.major,
running.minor,
if offered.is_empty() {
"nothing".to_owned()
} else {
offered.join(", ")
}
)
}