git_prole/git/refs/
branch.rs

1use 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/// A Git reference to a remote branch.
12#[derive(Debug, Clone, Hash, PartialEq, Eq)]
13pub enum BranchRef {
14    /// A local branch.
15    Local(LocalBranchRef),
16    /// A remote-tracking branch.
17    Remote(RemoteBranchRef),
18}
19
20impl BranchRef {
21    /// Get the qualified name of this branch.
22    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    /// Get the name of this branch.
30    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}