pub mod darcs;
pub mod git;
pub mod hg;
pub mod jj;
pub mod pijul;
use std::path::{Path, PathBuf};
use crate::{chunk::Chunk, options::Options, style::to_superscript};
use crate::providers::vcs::darcs::Darcs;
use crate::providers::vcs::git::Git;
use crate::providers::vcs::hg::Hg;
use crate::providers::vcs::jj::Jj;
use crate::providers::vcs::pijul::Pijul;
use enum_dispatch::enum_dispatch;
use itertools::Itertools;
use smallvec::SmallVec;
use smol_str::{SmolStr, SmolStrBuilder};
use tokio::fs;
#[enum_dispatch]
pub trait VcsTrait {
async fn branch(&self, opts: &Options, path: &Path) -> Option<Chunk<SmolStr>>;
async fn commit(&self, opts: &Options, path: &Path) -> Option<Chunk<SmolStr>>;
async fn status(&self, opts: &Options, path: &Path) -> Option<Chunk<SmolStr>>;
async fn worktree(&self, opts: &Options, path: &Path) -> Option<Chunk<SmolStr>>;
async fn stash(&self, opts: &Options, path: &Path) -> Option<Chunk<SmolStr>>;
async fn divergence(&self, opts: &Options, path: &Path) -> Option<Chunk<SmolStr>>;
}
#[enum_dispatch(VcsTrait)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Vcs {
Git,
Hg,
Jj,
Pijul,
Darcs,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
struct StatusIcon<T> {
pub value: &'static str,
pub marker: std::marker::PhantomData<T>,
}
impl<T> StatusIcon<T> {
pub const fn new(value: &'static str) -> Self {
Self {
value,
marker: std::marker::PhantomData,
}
}
}
impl<T> AsRef<str> for StatusIcon<T> {
fn as_ref(&self) -> &str {
self.value
}
}
pub async fn infer_vcs(start: PathBuf, opts: &Options) -> Option<(Vcs, PathBuf)> {
if !opts.vcs {
return None;
}
let mut dir = start.canonicalize().ok()?;
loop {
if fs::metadata(dir.join(".jj")).await.is_ok() {
return Some((Vcs::Jj(Jj), dir));
}
if fs::metadata(dir.join(".git")).await.is_ok() {
return Some((Vcs::Git(Git), dir));
}
if fs::metadata(dir.join(".hg")).await.is_ok() {
return Some((Vcs::Hg(Hg), dir));
}
if fs::metadata(dir.join(".pijul")).await.is_ok() {
return Some((Vcs::Pijul(Pijul), dir));
}
if fs::metadata(dir.join("_darcs")).await.is_ok() {
return Some((Vcs::Darcs(Darcs), dir));
}
if !dir.pop() {
break;
}
}
None
}
pub fn merge_icons<T: AsRef<str>, const N: usize>(icons: SmallVec<[T; N]>) -> SmolStr {
let mut builder = SmolStrBuilder::new();
icons
.iter()
.map(AsRef::<str>::as_ref)
.filter(|i| !i.is_empty()) .sorted()
.chunk_by(|icon| *icon) .into_iter()
.for_each(|(icon, group)| builder.push_str(&render_icon((icon, group.count()))));
builder.finish()
}
pub fn render_icon<T: AsRef<str>>((icon, n): (T, usize)) -> SmolStr {
let mut builder = SmolStrBuilder::new();
if n == 1 {
builder.push_str(icon.as_ref());
} else {
let mut buffer = itoa::Buffer::new();
let numb = buffer.format(n);
builder.push_str(icon.as_ref());
builder.push_str(&to_superscript(numb));
}
builder.finish()
}