git_prole/git/refs/
name.rsuse std::fmt::Debug;
use std::fmt::Display;
use std::str::FromStr;
use miette::miette;
use winnow::combinator::rest;
use winnow::token::take_till;
use winnow::PResult;
use winnow::Parser;
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Ref {
kind: String,
name: String,
}
impl Debug for Ref {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.to_string())
}
}
impl Ref {
pub const HEADS: &str = "heads";
pub const REMOTES: &str = "remotes";
pub const TAGS: &str = "tags";
pub fn new(kind: String, name: String) -> Self {
Self { kind, name }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn is_remote_branch(&self) -> bool {
self.kind == Self::REMOTES
}
pub fn is_local_branch(&self) -> bool {
self.kind == Self::HEADS
}
#[expect(dead_code)]
pub(crate) fn is_tag(&self) -> bool {
self.kind == Self::TAGS
}
pub fn parser(input: &mut &str) -> PResult<Self> {
let _refs_prefix = "refs/".parse_next(input)?;
let kind = take_till(1.., '/').parse_next(input)?;
let _ = '/'.parse_next(input)?;
let name = rest.parse_next(input)?;
Ok(Self {
kind: kind.to_owned(),
name: name.to_owned(),
})
}
}
impl FromStr for Ref {
type Err = miette::Report;
fn from_str(input: &str) -> Result<Self, Self::Err> {
Self::parser.parse(input).map_err(|err| miette!("{err}"))
}
}
impl Display for Ref {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "refs/{}/{}", self.kind, self.name)
} else {
write!(f, "{}", self.name)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ref_parse_no_slash() {
assert!(Ref::from_str("refs/puppy").is_err());
}
#[test]
fn test_ref_parse_simple() {
assert_eq!(
Ref::from_str("refs/puppy/doggy").unwrap(),
Ref {
kind: "puppy".into(),
name: "doggy".into()
}
);
}
#[test]
fn test_ref_parse_multiple_slashes() {
assert_eq!(
Ref::from_str("refs/puppy/doggy/softie/cutie").unwrap(),
Ref {
kind: "puppy".into(),
name: "doggy/softie/cutie".into()
}
);
}
}