use crate::command_options::OptionError;
use crate::diagnostics::EngineFailureCode;
use crate::policy_events::PolicyEvent;
use crate::repository_facts::RepositoryFacts;
use crate::rule_argument_rewrite::ArgumentRewrite;
use crate::rule_atomic_push::atomic_push;
use crate::rule_commit_only::{
CommitOnlyDecision, decide_commit_only, resolve_index_state, resolve_sequencer_state,
violation_code_text,
};
use crate::rule_status_hints::status_hints_off;
use std::ffi::OsString;
pub const COMMIT_ONLY_CORE: &str = "commit-only";
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TransformResult {
pub arguments: Vec<OsString>,
pub events: Vec<PolicyEvent>,
pub complete: bool,
}
fn incomplete(arguments: &[OsString], message: String) -> TransformResult {
return TransformResult {
arguments: arguments.to_vec(),
events: vec![PolicyEvent::EngineFailure {
code: EngineFailureCode::CoreIncomplete,
hint: crate::policy_events::EngineFailureHint::OfCode,
message,
trigger: None,
policy: None,
path: None,
}],
complete: false,
};
}
fn settle_commit_only(
decision: CommitOnlyDecision,
arguments: &[OsString],
facts: &mut dyn RepositoryFacts,
) -> Result<CommitOnlyDecision, String> {
match decision {
CommitOnlyDecision::NeedsSequencerState => {
return Ok(resolve_sequencer_state(facts.sequencer_state()?));
}
CommitOnlyDecision::NeedsIndexState(pending) => {
return Ok(resolve_index_state(
arguments,
&pending,
facts.index_vs_head()?,
));
}
other => return Ok(other),
}
}
fn commit_only_stage(
arguments: &[OsString],
escaped: bool,
facts: &mut dyn RepositoryFacts,
) -> TransformResult {
let unchanged: TransformResult = TransformResult {
arguments: arguments.to_vec(),
events: Vec::<PolicyEvent>::new(),
complete: true,
};
if escaped {
return unchanged;
}
let decided: Result<CommitOnlyDecision, OptionError> = decide_commit_only(arguments, &[]);
let Ok(first) = decided else {
return unchanged;
};
let settled: CommitOnlyDecision = match settle_commit_only(first, arguments, facts) {
Ok(decision) => decision,
Err(message) => return incomplete(arguments, message),
};
match settled {
CommitOnlyDecision::Unchanged => return unchanged,
CommitOnlyDecision::Rewritten(rewritten) => {
return TransformResult {
arguments: rewritten,
events: Vec::<PolicyEvent>::new(),
complete: true,
};
}
CommitOnlyDecision::Rejected(violation) => {
return TransformResult {
arguments: arguments.to_vec(),
events: vec![PolicyEvent::CoreFinding {
core_id: COMMIT_ONLY_CORE,
code: violation_code_text(violation.code),
message: violation.message,
}],
complete: true,
};
}
CommitOnlyDecision::NeedsSequencerState | CommitOnlyDecision::NeedsIndexState(_) => {
return incomplete(
arguments,
String::from(
"cli-git: the commit-only transform asked for a second repository fact \
and cannot decide.",
),
);
}
}
}
pub fn apply_fixed_transforms(
arguments: &[OsString],
commit_only_escaped: bool,
facts: &mut dyn RepositoryFacts,
) -> TransformResult {
let after_push: Vec<OsString> = match atomic_push(arguments, &[]) {
Ok(ArgumentRewrite::Rewritten(rewritten)) => rewritten,
Ok(ArgumentRewrite::Unchanged) | Err(_) => arguments.to_vec(),
};
let commit: TransformResult =
commit_only_stage(after_push.as_slice(), commit_only_escaped, facts);
if !commit.complete || !commit.events.is_empty() {
return TransformResult {
arguments: arguments.to_vec(),
events: commit.events,
complete: commit.complete,
};
}
let after_status: Vec<OsString> = match status_hints_off(commit.arguments.as_slice()) {
ArgumentRewrite::Rewritten(rewritten) => rewritten,
ArgumentRewrite::Unchanged => commit.arguments,
};
return TransformResult {
arguments: after_status,
events: Vec::<PolicyEvent>::new(),
complete: true,
};
}
#[cfg(test)]
#[path = "policy_transforms_tests.rs"]
mod tests;