git_prole/git/refs/
branch.rs1use std::fmt::Display;
2use std::ops::Deref;
3use std::str::FromStr;
4
5use miette::miette;
6
7use super::LocalBranchRef;
8use super::Ref;
9use super::RemoteBranchRef;
10
11#[derive(Debug, Clone, Hash, PartialEq, Eq)]
13pub enum BranchRef {
14 Local(LocalBranchRef),
16 Remote(RemoteBranchRef),
18}
19
20impl BranchRef {
21 pub fn qualified_branch_name(&self) -> &str {
23 match &self {
24 BranchRef::Local(ref_name) => ref_name.branch_name(),
25 BranchRef::Remote(ref_name) => ref_name.name(),
26 }
27 }
28
29 pub fn branch_name(&self) -> &str {
31 match &self {
32 BranchRef::Local(ref_name) => ref_name.branch_name(),
33 BranchRef::Remote(ref_name) => ref_name.branch_name(),
34 }
35 }
36
37 pub fn as_local(&self) -> LocalBranchRef {
38 match self {
39 BranchRef::Local(local) => local.clone(),
40 BranchRef::Remote(remote) => remote.as_local(),
41 }
42 }
43}
44
45impl PartialEq<Ref> for BranchRef {
46 fn eq(&self, other: &Ref) -> bool {
47 self.deref().eq(other)
48 }
49}
50
51impl Display for BranchRef {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 match self {
54 BranchRef::Local(ref_name) => Display::fmt(ref_name, f),
55 BranchRef::Remote(ref_name) => Display::fmt(ref_name, f),
56 }
57 }
58}
59
60impl Deref for BranchRef {
61 type Target = Ref;
62
63 fn deref(&self) -> &Self::Target {
64 match self {
65 BranchRef::Local(ref_name) => ref_name.deref(),
66 BranchRef::Remote(ref_name) => ref_name.deref(),
67 }
68 }
69}
70
71impl TryFrom<Ref> for BranchRef {
72 type Error = miette::Report;
73
74 fn try_from(value: Ref) -> Result<Self, Self::Error> {
75 match value.kind() {
76 Ref::HEADS => Ok(Self::Local(LocalBranchRef::try_from(value)?)),
77 Ref::REMOTES => Ok(Self::Remote(RemoteBranchRef::try_from(value)?)),
78 _ => Err(miette!("Ref is not a local or remote branch: {value}")),
79 }
80 }
81}
82
83impl FromStr for BranchRef {
84 type Err = miette::Report;
85
86 fn from_str(s: &str) -> Result<Self, Self::Err> {
87 Ref::from_str(s)?.try_into()
88 }
89}
90
91impl From<LocalBranchRef> for BranchRef {
92 fn from(value: LocalBranchRef) -> Self {
93 Self::Local(value)
94 }
95}
96
97impl From<RemoteBranchRef> for BranchRef {
98 fn from(value: RemoteBranchRef) -> Self {
99 Self::Remote(value)
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use std::str::FromStr;
106
107 use pretty_assertions::assert_eq;
108
109 use super::*;
110
111 #[test]
112 fn test_branch_ref_try_from() {
113 let branch = BranchRef::try_from(Ref::from_str("refs/heads/puppy/doggy").unwrap()).unwrap();
114
115 assert_eq!(branch.branch_name(), "puppy/doggy",);
116 assert_eq!(branch.qualified_branch_name(), "puppy/doggy",);
117
118 let branch =
119 BranchRef::try_from(Ref::from_str("refs/remotes/puppy/doggy").unwrap()).unwrap();
120
121 assert_eq!(branch.branch_name(), "doggy",);
122 assert_eq!(branch.qualified_branch_name(), "puppy/doggy",);
123
124 assert!(BranchRef::try_from(Ref::from_str("refs/tags/v1.0.0").unwrap()).is_err());
125 }
126
127 #[test]
128 fn test_branch_qualified_branch_name() {
129 assert_eq!(
130 BranchRef::Remote(RemoteBranchRef::new("origin", "puppy")).qualified_branch_name(),
131 "origin/puppy",
132 );
133
134 assert_eq!(
135 BranchRef::Local(LocalBranchRef::new("puppy".into())).qualified_branch_name(),
136 "puppy",
137 );
138 }
139
140 #[test]
141 fn test_branch_branch_name() {
142 assert_eq!(
143 BranchRef::Remote(RemoteBranchRef::new("origin", "puppy")).branch_name(),
144 "puppy",
145 );
146
147 assert_eq!(
148 BranchRef::Local(LocalBranchRef::new("puppy".into())).branch_name(),
149 "puppy",
150 );
151 }
152}