Skip to main content

ytil_tui/
git_branch.rs

1use std::fmt::Display;
2use std::ops::Deref;
3use std::path::Path;
4
5use owo_colors::OwoColorize as _;
6use rootcause::prelude::ResultExt as _;
7use ytil_git::branch::Branch;
8
9/// A wrapper around [`Branch`] for display purposes.
10struct RenderableBranch(pub Branch);
11
12impl Deref for RenderableBranch {
13    type Target = Branch;
14
15    fn deref(&self) -> &Self::Target {
16        &self.0
17    }
18}
19
20impl Display for RenderableBranch {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        let styled_date_time = format!("({})", self.committer_date_time());
23        let styled_email = format!("<{}>", self.committer_email());
24        write!(
25            f,
26            "{} {} {}",
27            self.name(),
28            styled_date_time.green(),
29            styled_email.blue().bold(),
30        )
31    }
32}
33
34/// Prompts the user to select a branch from all available branches.
35///
36/// The previously checked-out branch (`@{-1}`) is placed first when available.
37///
38/// # Errors
39/// - If repository discovery fails.
40/// - If [`ytil_git::branch::get_all_no_redundant`] fails.
41/// - If [`crate::minimal_select`] fails.
42pub fn select() -> rootcause::Result<Option<Branch>> {
43    let repo = ytil_git::repo::discover(Path::new(".")).context("error discovering repo for branch selection")?;
44    let mut branches = ytil_git::branch::get_all_no_redundant(&repo)?;
45
46    if let Some(prev) = ytil_git::branch::get_previous(&repo)
47        && let Some(idx) = branches.iter().position(|b| b.name_no_origin() == prev)
48    {
49        let branch = branches.remove(idx);
50        branches.insert(0, branch);
51    }
52
53    let Some(branch) = crate::minimal_select(branches.into_iter().map(RenderableBranch).collect())? else {
54        return Ok(None);
55    };
56
57    Ok(Some(branch.0))
58}