heroforge-core 0.2.2

Pure Rust core library for reading and writing Fossil SCM repositories
Documentation
//! Branch operations builder.

use crate::error::{FossilError, Result};
use crate::repo::{CheckIn, Repository};

/// Source for creating a branch.
enum BranchSource {
    Branch(String),
    Tag(String),
    Commit(String),
}

/// Builder for branch operations.
pub struct BranchesBuilder<'a> {
    repo: &'a Repository,
}

impl<'a> BranchesBuilder<'a> {
    pub(crate) fn new(repo: &'a Repository) -> Self {
        Self { repo }
    }

    /// List all branches.
    pub fn list(&self) -> Result<Vec<String>> {
        self.repo.list_branches_internal()
    }

    /// Get a specific branch.
    pub fn get(&self, name: &str) -> Result<BranchRef<'a>> {
        // Verify branch exists by getting its tip
        self.repo.branch_tip_internal(name)?;
        Ok(BranchRef {
            repo: self.repo,
            name: name.to_string(),
        })
    }

    /// Start creating a new branch.
    pub fn create(&self, name: &str) -> BranchBuilder<'a> {
        BranchBuilder {
            repo: self.repo,
            name: name.to_string(),
            source: None,
            author: None,
        }
    }
}

/// Reference to an existing branch.
pub struct BranchRef<'a> {
    repo: &'a Repository,
    name: String,
}

impl<'a> BranchRef<'a> {
    /// Get the tip (latest commit) of this branch.
    pub fn tip(&self) -> Result<CheckIn> {
        self.repo.branch_tip_internal(&self.name)
    }

    /// Get the branch name.
    pub fn name(&self) -> &str {
        &self.name
    }
}

/// Builder for creating a new branch.
pub struct BranchBuilder<'a> {
    repo: &'a Repository,
    name: String,
    source: Option<BranchSource>,
    author: Option<String>,
}

impl<'a> BranchBuilder<'a> {
    /// Create branch from another branch's tip.
    pub fn from_branch(mut self, branch: &str) -> Self {
        self.source = Some(BranchSource::Branch(branch.to_string()));
        self
    }

    /// Create branch from a tag.
    pub fn from_tag(mut self, tag: &str) -> Self {
        self.source = Some(BranchSource::Tag(tag.to_string()));
        self
    }

    /// Create branch from a specific commit.
    pub fn from_commit(mut self, hash: &str) -> Self {
        self.source = Some(BranchSource::Commit(hash.to_string()));
        self
    }

    /// Set the author for the branch creation commit.
    pub fn author(mut self, user: &str) -> Self {
        self.author = Some(user.to_string());
        self
    }

    /// Execute the branch creation.
    pub fn execute(self) -> Result<String> {
        let author = self
            .author
            .ok_or_else(|| FossilError::InvalidArtifact("branch author is required".to_string()))?;

        let source = self.source.ok_or_else(|| {
            FossilError::InvalidArtifact(
                "branch source is required (use from_branch, from_tag, or from_commit)".to_string(),
            )
        })?;

        let parent_hash = match source {
            BranchSource::Branch(name) => {
                let tip = self.repo.branch_tip_internal(&name)?;
                tip.hash
            }
            BranchSource::Tag(name) => self.repo.get_tag_checkin_internal(&name)?,
            BranchSource::Commit(hash) => hash,
        };

        self.repo
            .create_branch_internal(&self.name, &parent_hash, &author)
    }
}