use crate::cmd::CMD;
use crate::providers::vcs::{merge_icons, StatusIcon, VcsTrait};
use crate::{chunk::Chunk, options::Options};
use smallvec::SmallVec;
use smol_str::{format_smolstr, SmolStr, SmolStrBuilder, ToSmolStr};
use std::path::Path;
use std::str::FromStr;
use tokio::fs;
use tokio::io::AsyncReadExt;
macro_rules! hg {
( $( $x:expr ),* ) => {
CMD.exec("hg", [$( $x ),*])
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Hg;
impl VcsTrait for Hg {
async fn branch(&self, _opts: &Options, path: &Path) -> Option<Chunk<SmolStr>> {
let path = path.join(".hg").join("branch");
let branch = fs::read_to_string(path).await.ok()?;
Some(Chunk::new("hg ⎇", branch.trim().to_smolstr()))
}
async fn commit(&self, _opts: &Options, path: &Path) -> Option<Chunk<SmolStr>> {
let hash = get_hg_commit_hash(path).await?;
let bookmark_path = path.join(".hg").join("bookmarks.current");
let bookmark = fs::read_to_string(bookmark_path).await.ok();
match bookmark {
Some(bm) if !bm.trim().is_empty() => {
let combined = format_smolstr!("{} ({})", hash.trim(), bm.trim());
Some(Chunk::info(combined))
}
_ => Some(Chunk::info(hash)),
}
}
async fn status(&self, _opts: &Options, _path: &Path) -> Option<Chunk<SmolStr>> {
hg!("status").await.map(|status| {
Chunk::info(merge_icons(
status
.lines()
.map(|line| line.parse::<StatusIcon<Hg>>().unwrap())
.collect::<SmallVec<[_; 8]>>(),
))
})
}
async fn worktree(&self, _opts: &Options, path: &Path) -> Option<Chunk<SmolStr>> {
let path = path.join(".hg").join("sharedpath");
let sharedpath = fs::read_to_string(&path).await.ok()?;
sharedpath
.rfind('/')
.map(|pos| Chunk::new("⌂", sharedpath[..pos].into()))
}
async fn stash(&self, _opts: &Options, _path: &Path) -> Option<Chunk<SmolStr>> {
None
}
async fn divergence(&self, _opts: &Options, _path: &Path) -> Option<Chunk<SmolStr>> {
None
}
}
impl FromStr for StatusIcon<Hg> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut chars = s.chars();
match chars.next() {
Some('A') => Ok(StatusIcon::new("✚")), Some('M') => Ok(StatusIcon::new("●")), Some('R') => Ok(StatusIcon::new("✖")), Some('!') => Ok(StatusIcon::new("!")), Some('?') => Ok(StatusIcon::new("?")), Some('C') => Ok(StatusIcon::new("")), Some('I') => Ok(StatusIcon::new("")), _ => Ok(StatusIcon::new("")), }
}
}
async fn get_hg_commit_hash(base: &Path) -> Option<SmolStr> {
let dirstate_path = base.join(".hg").join("dirstate");
let mut hash_bytes = [0u8; 20];
let mut file = fs::File::open(dirstate_path).await.ok()?;
file.read_exact(&mut hash_bytes).await.ok()?;
let mut hex_string = SmolStrBuilder::new();
use std::fmt::Write;
for byte in &hash_bytes[..8] {
let _ = write!(hex_string, "{:02x}", byte);
}
hex_string.push('+');
Some(hex_string.finish())
}