use anyhow::{Result, anyhow};
use objects::{
error::HeddleError,
object::{ChangeId, State},
store::ObjectStore,
};
use repo::Repository;
use super::advice::RecoveryAdvice;
pub(crate) fn resolve_state_id(repo: &Repository, spec: &str) -> Result<ChangeId> {
match repo.resolve_state(spec)? {
Some(id) => Ok(id),
None => {
if let Some(tip) = repo.git_overlay_branch_tip(spec)?
&& !tip.history_imported
{
return Err(anyhow!(tip_only_branch_history_advice(&tip.branch)));
}
if let Some(tip) = repo.git_overlay_tag_tip(spec)?
&& !tip.history_imported
{
return Err(anyhow!(tip_only_tag_history_advice(&tip.tag)));
}
Err(anyhow!(state_not_found_advice(spec)))
}
}
}
pub(crate) fn require_resolved_state(repo: &Repository, id: &ChangeId) -> Result<State> {
repo.store().get_state(id)?.ok_or_else(|| {
anyhow::Error::new(HeddleError::MissingObject {
object_type: "state".to_string(),
id: id.to_string_full(),
})
})
}
fn state_not_found_advice(spec: &str) -> RecoveryAdvice {
RecoveryAdvice::safety_refusal(
"state_not_found",
format!("State not found: {spec}"),
"Inspect available states with `heddle log`, then retry with an existing state, marker, thread, or HEAD expression.",
format!("no Heddle state, marker, thread, or HEAD expression matched '{spec}'"),
"the command cannot move refs, inspect content, or write worktree files until the target state is resolved",
"repository state and worktree files were left unchanged",
"heddle log",
vec!["heddle log".to_string()],
)
}
fn tip_only_branch_history_advice(branch: &str) -> RecoveryAdvice {
let import_command = super::git_overlay_health::canonical_adopt_ref_command(branch);
RecoveryAdvice::safety_refusal(
"git_branch_history_not_imported",
format!("Heddle has not imported Git branch '{branch}' history yet"),
format!("Import its history first with `{import_command}`."),
format!("branch '{branch}' has a Git tip but no imported Heddle history"),
"history-sensitive commands cannot safely resolve this branch until Heddle imports its Git history",
"repository state and worktree files were left unchanged",
import_command.clone(),
vec![import_command],
)
}
fn tip_only_tag_history_advice(tag: &str) -> RecoveryAdvice {
let import_command = super::git_overlay_health::canonical_adopt_ref_command(tag);
RecoveryAdvice::safety_refusal(
"git_tag_history_not_imported",
format!("Git tag '{tag}' is visible but its history is not imported yet"),
format!("Import it first with `{import_command}`."),
format!("tag '{tag}' has a Git tip but no imported Heddle history"),
"history-sensitive commands cannot safely resolve this tag to a Heddle state yet",
"repository state and worktree files were left unchanged",
import_command.clone(),
vec![import_command],
)
}
pub(crate) fn resolve_state_id_bytes(repo: &Repository, spec: &str) -> Result<Vec<u8>> {
Ok(resolve_state_id(repo, spec)?.as_bytes().to_vec())
}