git_prole/git/refs/
name.rs1use std::fmt::Debug;
2use std::fmt::Display;
3use std::str::FromStr;
4
5use miette::miette;
6use winnow::combinator::rest;
7use winnow::token::take_till;
8use winnow::PResult;
9use winnow::Parser;
10
11#[derive(Clone, Hash, PartialEq, Eq)]
18pub struct Ref {
19 kind: String,
25 name: String,
27}
28
29impl Debug for Ref {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "{:?}", self.to_string())
32 }
33}
34
35impl Ref {
36 pub const HEADS: &str = "heads";
38 pub const REMOTES: &str = "remotes";
40 pub const TAGS: &str = "tags";
42
43 pub fn new(kind: String, name: String) -> Self {
44 Self { kind, name }
45 }
46
47 pub fn name(&self) -> &str {
48 &self.name
49 }
50
51 pub fn kind(&self) -> &str {
52 &self.kind
53 }
54
55 pub fn is_remote_branch(&self) -> bool {
57 self.kind == Self::REMOTES
58 }
59
60 pub fn is_local_branch(&self) -> bool {
62 self.kind == Self::HEADS
63 }
64
65 #[expect(dead_code)]
67 pub(crate) fn is_tag(&self) -> bool {
68 self.kind == Self::TAGS
69 }
70
71 pub fn parser(input: &mut &str) -> PResult<Self> {
76 let _refs_prefix = "refs/".parse_next(input)?;
77
78 let kind = take_till(1.., '/').parse_next(input)?;
79 let _ = '/'.parse_next(input)?;
80 let name = rest.parse_next(input)?;
81
82 Ok(Self {
83 kind: kind.to_owned(),
84 name: name.to_owned(),
85 })
86 }
87}
88
89impl FromStr for Ref {
90 type Err = miette::Report;
91
92 fn from_str(input: &str) -> Result<Self, Self::Err> {
93 Self::parser.parse(input).map_err(|err| miette!("{err}"))
94 }
95}
96
97impl Display for Ref {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 if f.alternate() {
100 write!(f, "refs/{}/{}", self.kind, self.name)
101 } else {
102 write!(f, "{}", self.name)
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_ref_parse_no_slash() {
113 assert!(Ref::from_str("refs/puppy").is_err());
114 }
115
116 #[test]
117 fn test_ref_parse_simple() {
118 assert_eq!(
119 Ref::from_str("refs/puppy/doggy").unwrap(),
120 Ref {
121 kind: "puppy".into(),
122 name: "doggy".into()
123 }
124 );
125 }
126
127 #[test]
128 fn test_ref_parse_multiple_slashes() {
129 assert_eq!(
130 Ref::from_str("refs/puppy/doggy/softie/cutie").unwrap(),
131 Ref {
132 kind: "puppy".into(),
133 name: "doggy/softie/cutie".into()
134 }
135 );
136 }
137}