use std::collections::HashSet;
use anyhow::{Context as _, Result};
use crate::config::CrateConfig;
use crate::context::Context;
const PER_CRATE_SCOPED_VARS: &[&str] = &["Version", "RawVersion", "Tag", "ProjectName", "Name"];
pub fn crate_template_overrides(name: &str, tag: &str) -> Result<Vec<(&'static str, String)>> {
let semver = crate::git::parse_semver_tag(tag).with_context(|| {
format!("crate '{name}': release tag '{tag}' is not a parseable semver version")
})?;
Ok(vec![
("RawVersion", semver.raw_version_string()),
("Version", semver.version_string()),
("Tag", tag.to_string()),
("ProjectName", name.to_string()),
("Name", name.to_string()),
])
}
pub fn resolve_crate_tag(ctx: &Context, crate_cfg: &CrateConfig) -> Option<String> {
let monorepo_prefix = ctx.config.monorepo_tag_prefix();
let repo = ctx
.options
.project_root
.clone()
.unwrap_or_else(|| std::path::PathBuf::from("."));
let tag = crate::git::find_latest_tag_matching_with_prefix_in(
&repo,
&crate_cfg.tag_template,
ctx.config.git.as_ref(),
Some(ctx.template_vars()),
monorepo_prefix,
)
.ok()
.flatten()?;
let stripped = match monorepo_prefix {
Some(prefix) => crate::git::strip_monorepo_prefix(&tag, prefix).to_string(),
None => tag,
};
Some(stripped)
}
pub fn apply_var_overrides(
ctx: &mut Context,
overrides: &[(&'static str, String)],
) -> Vec<(&'static str, Option<String>)> {
let mut saved: Vec<(&'static str, Option<String>)> = Vec::new();
let mut seen: HashSet<&'static str> = HashSet::new();
for key in PER_CRATE_SCOPED_VARS {
if seen.insert(*key) {
saved.push((*key, ctx.template_vars().get(key).cloned()));
}
}
for (key, value) in overrides {
ctx.template_vars_mut().set(key, value);
}
saved
}
pub fn restore_var_overrides(ctx: &mut Context, saved: Vec<(&'static str, Option<String>)>) {
for (key, prior) in saved {
match prior {
Some(value) => ctx.template_vars_mut().set(key, &value),
None => {
ctx.template_vars_mut().unset(key);
}
}
}
}
pub fn with_crate_scope<T>(
ctx: &mut Context,
crate_cfg: &CrateConfig,
resolve_tag: &dyn Fn(&Context, &CrateConfig) -> Option<String>,
body: impl FnOnce(&mut Context) -> Result<T>,
) -> Result<T> {
let tag = resolve_tag(ctx, crate_cfg).with_context(|| {
format!(
"crate '{}' is selected for a per-crate emission but has no \
release tag matching its tag_template; cannot derive its version",
crate_cfg.name
)
})?;
let overrides = crate_template_overrides(&crate_cfg.name, &tag)?;
let saved = apply_var_overrides(ctx, &overrides);
let result = body(ctx);
restore_var_overrides(ctx, saved);
result
}