git_prole/git/refs/
local_branch.rs

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