branch_party_core 0.1.1

Core library for branch-party CLI tool
Documentation
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Config {
    #[serde(default = "default_base_branch")]
    pub base_branch: String,

    #[serde(default)]
    pub parties: HashMap<String, Party>,

    #[serde(default)]
    pub auto_update: AutoUpdateConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AutoUpdateConfig {
    #[serde(default)]
    pub enabled: bool,

    #[serde(default)]
    pub parties: AutoUpdateParties,

    #[serde(default)]
    pub push: bool,

    #[serde(default)]
    pub quiet: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum AutoUpdateParties {
    All(String), // "all"
    Specific(Vec<String>),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Party {
    #[serde(default)]
    pub members: Vec<String>,

    #[serde(default)]
    pub merge_order: MergeOrderStrategy,

    #[serde(default)]
    pub conflict_policy: ConflictPolicy,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum MergeOrderStrategy {
    Listed,
    NewestFirst,
    OldestFirst,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ConflictPolicy {
    #[serde(default = "default_conflict_mode")]
    pub default: ConflictMode,

    #[serde(default)]
    pub overrides: Vec<ConflictOverride>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ConflictOverride {
    pub path: String,
    pub mode: ConflictMode,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ConflictMode {
    Ours,
    Theirs,
    Union,
    Manual,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            base_branch: default_base_branch(),
            parties: HashMap::new(),
            auto_update: AutoUpdateConfig::default(),
        }
    }
}

impl Default for Party {
    fn default() -> Self {
        Self {
            members: Vec::new(),
            merge_order: MergeOrderStrategy::default(),
            conflict_policy: ConflictPolicy::default(),
        }
    }
}

impl Default for MergeOrderStrategy {
    fn default() -> Self {
        MergeOrderStrategy::Listed
    }
}

impl Default for ConflictPolicy {
    fn default() -> Self {
        Self {
            default: default_conflict_mode(),
            overrides: Vec::new(),
        }
    }
}

impl Default for ConflictMode {
    fn default() -> Self {
        ConflictMode::Manual
    }
}

impl Default for AutoUpdateConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            parties: AutoUpdateParties::default(),
            push: false,
            quiet: false,
        }
    }
}

impl Default for AutoUpdateParties {
    fn default() -> Self {
        AutoUpdateParties::All("all".to_string())
    }
}

fn default_base_branch() -> String {
    "main".to_string()
}

fn default_conflict_mode() -> ConflictMode {
    ConflictMode::Manual
}

impl Config {
    pub fn sample() -> Self {
        let mut parties = HashMap::new();
        
        parties.insert("qa".to_string(), Party {
            members: vec![
                "feature/a".to_string(),
                "feature/b".to_string(),
                "@payments".to_string(),
            ],
            merge_order: MergeOrderStrategy::Listed,
            conflict_policy: ConflictPolicy {
                default: ConflictMode::Ours,
                overrides: vec![
                    ConflictOverride {
                        path: "Gemfile.lock".to_string(),
                        mode: ConflictMode::Ours,
                    },
                    ConflictOverride {
                        path: "yarn.lock".to_string(),
                        mode: ConflictMode::Theirs,
                    },
                ],
            },
        });

        parties.insert("payments".to_string(), Party {
            members: vec![
                "feature/pay-ux".to_string(),
                "feature/pay-api".to_string(),
            ],
            merge_order: MergeOrderStrategy::Listed,
            conflict_policy: ConflictPolicy::default(),
        });

        Self {
            base_branch: "main".to_string(),
            parties,
            auto_update: AutoUpdateConfig {
                enabled: true,
                parties: AutoUpdateParties::All("all".to_string()),
                push: false,
                quiet: true,
            },
        }
    }
}

impl std::fmt::Display for MergeOrderStrategy {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MergeOrderStrategy::Listed => write!(f, "listed"),
            MergeOrderStrategy::NewestFirst => write!(f, "newest_first"),
            MergeOrderStrategy::OldestFirst => write!(f, "oldest_first"),
        }
    }
}

impl std::str::FromStr for MergeOrderStrategy {
    type Err = crate::error::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "listed" => Ok(MergeOrderStrategy::Listed),
            "newest_first" => Ok(MergeOrderStrategy::NewestFirst),
            "oldest_first" => Ok(MergeOrderStrategy::OldestFirst),
            _ => Err(crate::error::Error::config(format!("Invalid merge order strategy: {}", s))),
        }
    }
}

impl std::fmt::Display for ConflictMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ConflictMode::Ours => write!(f, "ours"),
            ConflictMode::Theirs => write!(f, "theirs"),
            ConflictMode::Union => write!(f, "union"),
            ConflictMode::Manual => write!(f, "manual"),
        }
    }
}

impl std::str::FromStr for ConflictMode {
    type Err = crate::error::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ours" => Ok(ConflictMode::Ours),
            "theirs" => Ok(ConflictMode::Theirs),
            "union" => Ok(ConflictMode::Union),
            "manual" => Ok(ConflictMode::Manual),
            _ => Err(crate::error::Error::invalid_conflict_strategy(s)),
        }
    }
}