use std::collections::HashMap;
use git2::BranchType;
use git2::Oid;
use git2::Repository;
use crate::layout;
use crate::print;
use crate::settings::Settings;
#[derive(Default)]
pub struct LabelMap {
labels: HashMap<Oid, Vec<Label>>,
}
#[derive(Clone, Default)]
pub struct Label {
pub name: String,
pub kind: LabelType,
pub term_color: u8,
pub svg_color: String,
}
#[derive(Clone, Default, PartialEq)]
pub enum LabelType {
#[default]
LocalBranch,
RemoteBranch,
Tag,
}
impl LabelMap {
pub fn add_label<T: Into<String>>(
&mut self,
oid: Oid,
name: T,
kind: LabelType,
term_color: u8,
svg_color: String,
) {
let name = name.into();
let label_list = self.labels.entry(oid).or_insert(vec![]);
label_list.push(Label {
name,
kind,
term_color,
svg_color,
});
}
pub fn get_labels(&self, oid: &Oid) -> Option<&Vec<Label>> {
self.labels.get(oid)
}
}
pub fn list_labels(settings: &Settings, repository: &Repository) -> Result<LabelMap, String> {
let include_remote = settings.include_remote;
let mut labels = LabelMap::default();
extract_branches(&mut labels, settings, repository, include_remote)?;
extract_tags(&mut labels, settings, repository)?;
Ok(labels)
}
type TermSvgColor = (
u8,
String,
);
fn extract_branches(
labels: &mut LabelMap,
settings: &Settings,
repository: &Repository,
include_remote: bool,
) -> Result<(), String> {
let filter = if include_remote {
None
} else {
Some(BranchType::Local)
};
let actual_branches = repository
.branches(filter)
.map_err(|err| err.message().to_string())?
.collect::<Result<Vec<_>, _>>()
.map_err(|err| err.message().to_string())?;
let mut counter: usize = 0;
for (br, tp) in actual_branches {
let Some(n) = br.get().name() else {
continue;
};
let Some(t) = br.get().target() else {
continue;
};
let start_index = match tp {
BranchType::Local => 11, BranchType::Remote => 13, };
let name = &n[start_index..];
let label_type = match tp {
BranchType::Local => LabelType::LocalBranch,
BranchType::Remote => LabelType::RemoteBranch,
};
let (term_color, svg_color) = get_term_svg_color(settings, name, counter);
counter += 1;
labels.add_label(t.clone(), name, label_type, term_color, svg_color);
}
Ok(())
}
fn extract_tags(
labels: &mut LabelMap,
settings: &Settings,
repository: &Repository,
) -> Result<(), String> {
let mut tags_raw = Vec::new();
repository
.tag_foreach(|oid, name| {
tags_raw.push((oid, name.to_vec()));
true })
.map_err(|err| err.message().to_string())?;
let mut counter: usize = 0;
for (oid, name_bytes) in tags_raw {
let name = std::str::from_utf8(&name_bytes[5..]).map_err(|err| err.to_string())?;
let target = repository
.find_tag(oid)
.map(|tag| tag.target_id())
.or_else(|_| repository.find_commit(oid).map(|_| oid))
.map_err(|err| err.to_string())?;
let oid = target.clone();
let (term_color, svg_color) = get_term_svg_color(settings, name, counter);
counter += 1;
labels.add_label(oid, name, LabelType::Tag, term_color, svg_color);
}
Ok(())
}
fn get_term_svg_color(settings: &Settings, name_to_color: &str, idx: usize) -> TermSvgColor {
let term_color_str = layout::branch_color(
name_to_color,
&settings.branches.terminal_colors,
&settings.branches.terminal_colors_unknown,
idx,
);
let term_color =
print::colors::to_terminal_color(&term_color_str).expect("Valid terminal color string");
let svg_color = layout::branch_color(
name_to_color,
&settings.branches.svg_colors,
&settings.branches.svg_colors_unknown,
idx,
);
(term_color, svg_color)
}