use crate::config::{ConflictPolicy, MergeOrderStrategy};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
pub struct BranchSpec {
pub name: String,
pub is_party: bool,
}
impl BranchSpec {
pub fn branch(name: impl Into<String>) -> Self {
Self {
name: name.into(),
is_party: false,
}
}
pub fn party(name: impl Into<String>) -> Self {
Self {
name: name.into(),
is_party: true,
}
}
pub fn parse(spec: impl AsRef<str>) -> Self {
let spec = spec.as_ref();
if spec.starts_with('@') {
Self::party(&spec[1..])
} else {
Self::branch(spec)
}
}
}
impl std::fmt::Display for BranchSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_party {
write!(f, "@{}", self.name)
} else {
write!(f, "{}", self.name)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MergePlan {
pub party_name: String,
pub destination: String,
pub base_branch: String,
pub sources: Vec<String>,
pub strategy: MergeOrderStrategy,
pub conflict_policy: ConflictPolicy,
pub steps: Vec<MergeStep>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MergeStep {
pub source: String,
pub target: String,
pub expected_conflicts: Vec<String>,
pub is_fast_forward: bool,
}
impl MergePlan {
pub fn new(
party_name: String,
destination: String,
base_branch: String,
sources: Vec<String>,
strategy: MergeOrderStrategy,
conflict_policy: ConflictPolicy,
) -> Self {
Self {
party_name,
destination,
base_branch,
sources,
strategy,
conflict_policy,
steps: Vec::new(),
}
}
pub fn add_step(&mut self, step: MergeStep) {
self.steps.push(step);
}
pub fn total_sources(&self) -> usize {
self.sources.len()
}
pub fn estimated_conflicts(&self) -> usize {
self.steps.iter().map(|s| s.expected_conflicts.len()).sum()
}
pub fn fast_forward_count(&self) -> usize {
self.steps.iter().filter(|s| s.is_fast_forward).count()
}
}
impl MergeStep {
pub fn new(source: String, target: String) -> Self {
Self {
source,
target,
expected_conflicts: Vec::new(),
is_fast_forward: false,
}
}
pub fn with_conflicts(mut self, conflicts: Vec<String>) -> Self {
self.expected_conflicts = conflicts;
self
}
pub fn as_fast_forward(mut self) -> Self {
self.is_fast_forward = true;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RepoState {
pub current_branch: String,
pub is_clean: bool,
pub uncommitted_changes: Vec<String>,
pub branch_exists: bool,
pub commit_hash: String,
}
impl RepoState {
pub fn new(
current_branch: String,
is_clean: bool,
uncommitted_changes: Vec<String>,
branch_exists: bool,
commit_hash: String,
) -> Self {
Self {
current_branch,
is_clean,
uncommitted_changes,
branch_exists,
commit_hash,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_branch_spec_parsing() {
let branch = BranchSpec::parse("feature/test");
assert!(!branch.is_party);
assert_eq!(branch.name, "feature/test");
let party = BranchSpec::parse("@payments");
assert!(party.is_party);
assert_eq!(party.name, "payments");
}
#[test]
fn test_branch_spec_display() {
let branch = BranchSpec::branch("main");
assert_eq!(branch.to_string(), "main");
let party = BranchSpec::party("qa");
assert_eq!(party.to_string(), "@qa");
}
#[test]
fn test_merge_plan_creation() {
let plan = MergePlan::new(
"qa".to_string(),
"party/qa".to_string(),
"main".to_string(),
vec!["feature/a".to_string(), "feature/b".to_string()],
MergeOrderStrategy::Listed,
ConflictPolicy::default(),
);
assert_eq!(plan.party_name, "qa");
assert_eq!(plan.total_sources(), 2);
assert_eq!(plan.estimated_conflicts(), 0);
assert_eq!(plan.fast_forward_count(), 0);
}
}