git_prole/git/refs/
local_branch.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use std::fmt::Debug;
use std::fmt::Display;
use std::ops::Deref;

use miette::miette;

use super::Ref;
use super::RemoteBranchRef;

/// A Git reference to a local branch.
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct LocalBranchRef(Ref);

impl Debug for LocalBranchRef {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(&self.0, f)
    }
}

impl PartialEq<Ref> for LocalBranchRef {
    fn eq(&self, other: &Ref) -> bool {
        self.0.eq(other)
    }
}

impl LocalBranchRef {
    pub fn new(name: String) -> Self {
        Self(Ref::new(Ref::HEADS.to_owned(), name))
    }

    /// Get the name of this local branch.
    pub fn branch_name(&self) -> &str {
        self.0.name()
    }

    pub fn on_remote(&self, remote: &str) -> RemoteBranchRef {
        RemoteBranchRef::new(remote, self.branch_name())
    }
}

impl Deref for LocalBranchRef {
    type Target = Ref;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TryFrom<Ref> for LocalBranchRef {
    type Error = miette::Report;

    fn try_from(value: Ref) -> Result<Self, Self::Error> {
        if value.is_local_branch() {
            Ok(Self(value))
        } else {
            Err(miette!("Ref is not a local branch: {value}"))
        }
    }
}

impl<S> From<S> for LocalBranchRef
where
    S: AsRef<str>,
{
    fn from(value: S) -> Self {
        Self::new(value.as_ref().to_owned())
    }
}

impl Display for LocalBranchRef {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Display::fmt(&self.0, f)
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use pretty_assertions::assert_eq;

    use super::*;

    #[test]
    fn local_branch_ref_try_from() {
        let branch =
            LocalBranchRef::try_from(Ref::from_str("refs/heads/puppy/doggy").unwrap()).unwrap();

        assert_eq!(branch.branch_name(), "puppy/doggy");
    }

    #[test]
    fn local_branch_ref_from_str() {
        let branch = LocalBranchRef::from("puppy");

        assert_eq!(branch, Ref::from_str("refs/heads/puppy").unwrap());
    }

    #[test]
    fn test_local_branch_new() {
        assert_eq!(
            LocalBranchRef::new("puppy".into()),
            Ref::from_str("refs/heads/puppy").unwrap(),
        );
    }

    #[test]
    fn test_local_branch_branch_name() {
        assert_eq!(LocalBranchRef::new("puppy".into()).branch_name(), "puppy",);
    }

    #[test]
    fn test_local_branch_on_remote() {
        assert_eq!(
            LocalBranchRef::new("puppy".into()).on_remote("origin"),
            Ref::from_str("refs/remotes/origin/puppy").unwrap(),
        );
    }

    #[test]
    fn test_remote_branch_display() {
        let branch = LocalBranchRef::new("puppy".into());
        assert_eq!(format!("{branch}"), "puppy");
        assert_eq!(format!("{branch:#}"), "refs/heads/puppy");
    }
}