mirror of
https://github.com/Llewellynvdm/starship.git
synced 2024-09-27 20:59:02 +00:00
155 lines
5.2 KiB
Rust
155 lines
5.2 KiB
Rust
use git2::RepositoryState;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
|
use crate::configs::git_state::GitStateConfig;
|
|
|
|
/// Creates a module with the state of the git repository at the current directory
|
|
///
|
|
/// During a git operation it will show: REBASING, BISECTING, MERGING, etc.
|
|
/// If the progress information is available (e.g. rebasing 3/10), it will show that too.
|
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|
let mut module = context.new_module("git_state");
|
|
let config: GitStateConfig = GitStateConfig::try_load(module.config);
|
|
|
|
module.set_style(config.style);
|
|
module.get_prefix().set_value("(");
|
|
module.get_suffix().set_value(") ");
|
|
|
|
let repo = context.get_repo().ok()?;
|
|
let repo_root = repo.root.as_ref()?;
|
|
let repo_state = repo.state?;
|
|
|
|
let state_description = get_state_description(repo_state, repo_root, config);
|
|
|
|
let label = match &state_description {
|
|
StateDescription::Label(label) => label,
|
|
StateDescription::LabelAndProgress(label, _) => label,
|
|
StateDescription::Clean => {
|
|
return None;
|
|
}
|
|
};
|
|
|
|
module.create_segment(label.name, &label.segment);
|
|
|
|
if let StateDescription::LabelAndProgress(_, progress) = &state_description {
|
|
module.create_segment(
|
|
"progress_current",
|
|
&SegmentConfig::new(&format!(" {}", progress.current)),
|
|
);
|
|
module.create_segment("progress_divider", &SegmentConfig::new("/"));
|
|
module.create_segment(
|
|
"progress_total",
|
|
&SegmentConfig::new(&format!("{}", progress.total)),
|
|
);
|
|
}
|
|
|
|
Some(module)
|
|
}
|
|
|
|
/// Returns the state of the current repository
|
|
///
|
|
/// During a git operation it will show: REBASING, BISECTING, MERGING, etc.
|
|
fn get_state_description<'a>(
|
|
state: RepositoryState,
|
|
root: &'a std::path::PathBuf,
|
|
config: GitStateConfig<'a>,
|
|
) -> StateDescription<'a> {
|
|
match state {
|
|
RepositoryState::Clean => StateDescription::Clean,
|
|
RepositoryState::Merge => StateDescription::Label(StateLabel::new("merge", config.merge)),
|
|
RepositoryState::Revert => {
|
|
StateDescription::Label(StateLabel::new("revert", config.revert))
|
|
}
|
|
RepositoryState::RevertSequence => {
|
|
StateDescription::Label(StateLabel::new("revert", config.revert))
|
|
}
|
|
RepositoryState::CherryPick => {
|
|
StateDescription::Label(StateLabel::new("cherry_pick", config.cherry_pick))
|
|
}
|
|
RepositoryState::CherryPickSequence => {
|
|
StateDescription::Label(StateLabel::new("cherry_pick", config.cherry_pick))
|
|
}
|
|
RepositoryState::Bisect => {
|
|
StateDescription::Label(StateLabel::new("bisect", config.bisect))
|
|
}
|
|
RepositoryState::ApplyMailbox => StateDescription::Label(StateLabel::new("am", config.am)),
|
|
RepositoryState::ApplyMailboxOrRebase => {
|
|
StateDescription::Label(StateLabel::new("am_or_rebase", config.am_or_rebase))
|
|
}
|
|
RepositoryState::Rebase => describe_rebase(root, config.rebase),
|
|
RepositoryState::RebaseInteractive => describe_rebase(root, config.rebase),
|
|
RepositoryState::RebaseMerge => describe_rebase(root, config.rebase),
|
|
}
|
|
}
|
|
|
|
fn describe_rebase<'a>(
|
|
root: &'a PathBuf,
|
|
rebase_config: SegmentConfig<'a>,
|
|
) -> StateDescription<'a> {
|
|
/*
|
|
* Sadly, libgit2 seems to have some issues with reading the state of
|
|
* interactive rebases. So, instead, we'll poke a few of the .git files
|
|
* ourselves. This might be worth re-visiting this in the future...
|
|
*
|
|
* The following is based heavily on: https://github.com/magicmonty/bash-git-prompt
|
|
*/
|
|
|
|
let dot_git = root.join(".git");
|
|
|
|
let has_path = |relative_path: &str| {
|
|
let path = dot_git.join(Path::new(relative_path));
|
|
path.exists()
|
|
};
|
|
|
|
let file_to_usize = |relative_path: &str| {
|
|
let path = dot_git.join(Path::new(relative_path));
|
|
let contents = crate::utils::read_file(path).ok()?;
|
|
let quantity = contents.trim().parse::<usize>().ok()?;
|
|
Some(quantity)
|
|
};
|
|
|
|
let paths_to_progress = |current_path: &str, total_path: &str| {
|
|
let current = file_to_usize(current_path)?;
|
|
let total = file_to_usize(total_path)?;
|
|
Some(StateProgress { current, total })
|
|
};
|
|
|
|
let progress = if has_path("rebase-merge") {
|
|
paths_to_progress("rebase-merge/msgnum", "rebase-merge/end")
|
|
} else if has_path("rebase-apply") {
|
|
paths_to_progress("rebase-apply/next", "rebase-apply/last")
|
|
} else {
|
|
None
|
|
};
|
|
|
|
match progress {
|
|
None => StateDescription::Label(StateLabel::new("rebase", rebase_config)),
|
|
Some(progress) => {
|
|
StateDescription::LabelAndProgress(StateLabel::new("rebase", rebase_config), progress)
|
|
}
|
|
}
|
|
}
|
|
|
|
enum StateDescription<'a> {
|
|
Clean,
|
|
Label(StateLabel<'a>),
|
|
LabelAndProgress(StateLabel<'a>, StateProgress),
|
|
}
|
|
|
|
struct StateLabel<'a> {
|
|
name: &'static str,
|
|
segment: SegmentConfig<'a>,
|
|
}
|
|
|
|
struct StateProgress {
|
|
current: usize,
|
|
total: usize,
|
|
}
|
|
|
|
impl<'a> StateLabel<'a> {
|
|
fn new(name: &'static str, segment: SegmentConfig<'a>) -> Self {
|
|
Self { name, segment }
|
|
}
|
|
}
|