git_prole/git/refs/
remote_branch.rs

1use std::fmt::Debug;
2use std::fmt::Display;
3use std::ops::Deref;
4
5use miette::miette;
6
7use super::LocalBranchRef;
8use super::Ref;
9
10/// A Git reference to a remote branch.
11#[derive(Clone, Hash, PartialEq, Eq)]
12pub struct RemoteBranchRef(Ref);
13
14impl Debug for RemoteBranchRef {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        Debug::fmt(&self.0, f)
17    }
18}
19
20impl PartialEq<Ref> for RemoteBranchRef {
21    fn eq(&self, other: &Ref) -> bool {
22        self.0.eq(other)
23    }
24}
25
26impl RemoteBranchRef {
27    pub fn new(remote: &str, name: &str) -> Self {
28        Self(Ref::new(
29            Ref::REMOTES.to_owned(),
30            format!("{remote}/{name}"),
31        ))
32    }
33
34    /// Get the qualified name of this branch, including the remote name.
35    pub fn qualified_branch_name(&self) -> &str {
36        self.name()
37    }
38
39    /// Get the name of this remote and branch.
40    pub fn remote_and_branch(&self) -> (&str, &str) {
41        self.0
42            .name()
43            .split_once('/')
44            .expect("A remote branch always has a remote and a branch")
45    }
46
47    /// Get the name of this remote.
48    pub fn remote(&self) -> &str {
49        self.remote_and_branch().0
50    }
51
52    /// Get the name of this branch.
53    pub fn branch_name(&self) -> &str {
54        self.remote_and_branch().1
55    }
56
57    /// Get a local branch with the same name.
58    pub fn as_local(&self) -> LocalBranchRef {
59        LocalBranchRef::new(self.branch_name().to_owned())
60    }
61}
62
63impl Deref for RemoteBranchRef {
64    type Target = Ref;
65
66    fn deref(&self) -> &Self::Target {
67        &self.0
68    }
69}
70
71impl TryFrom<Ref> for RemoteBranchRef {
72    type Error = miette::Report;
73
74    fn try_from(value: Ref) -> Result<Self, Self::Error> {
75        if value.is_remote_branch() {
76            Ok(Self(value))
77        } else {
78            Err(miette!("Ref is not a remote branch: {value}"))
79        }
80    }
81}
82
83impl Display for RemoteBranchRef {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        Display::fmt(&self.0, f)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use std::str::FromStr;
92
93    use pretty_assertions::assert_eq;
94
95    use super::*;
96
97    #[test]
98    fn remote_branch_ref_try_from() {
99        let branch =
100            RemoteBranchRef::try_from(Ref::from_str("refs/remotes/puppy/doggy").unwrap()).unwrap();
101
102        assert_eq!(branch.remote(), "puppy");
103        assert_eq!(branch.branch_name(), "doggy");
104    }
105
106    #[test]
107    fn test_remote_branch_new() {
108        assert_eq!(
109            RemoteBranchRef::new("origin", "puppy"),
110            Ref::from_str("refs/remotes/origin/puppy").unwrap(),
111        );
112    }
113
114    #[test]
115    fn test_remote_branch_qualified_branch_name() {
116        assert_eq!(
117            RemoteBranchRef::new("origin", "puppy").qualified_branch_name(),
118            "origin/puppy",
119        );
120    }
121
122    #[test]
123    fn test_remote_branch_remote_and_branch() {
124        assert_eq!(
125            RemoteBranchRef::new("origin", "puppy/doggy").remote_and_branch(),
126            ("origin", "puppy/doggy"),
127        );
128    }
129
130    #[test]
131    fn test_remote_branch_branch_name() {
132        assert_eq!(
133            RemoteBranchRef::new("origin", "puppy").branch_name(),
134            "puppy",
135        );
136    }
137
138    #[test]
139    fn test_remote_branch_as_local() {
140        assert_eq!(
141            RemoteBranchRef::new("origin", "puppy").as_local(),
142            Ref::from_str("refs/heads/puppy").unwrap(),
143        );
144    }
145
146    #[test]
147    fn test_remote_branch_display() {
148        let branch = RemoteBranchRef::new("origin", "puppy");
149        assert_eq!(format!("{branch}"), "origin/puppy");
150        assert_eq!(format!("{branch:#}"), "refs/remotes/origin/puppy");
151    }
152}