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
127
128
129
130
131
132
133
use std::{convert::TryFrom, path::Path};

use git_object::bstr::{BStr, BString, ByteSlice};

use crate::{bstr::ByteVec, FullName, FullNameRef, Namespace};

impl TryFrom<&str> for FullName {
    type Error = git_validate::refname::Error;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        Ok(FullName(git_validate::refname(value.as_bytes().as_bstr())?.into()))
    }
}

impl TryFrom<String> for FullName {
    type Error = git_validate::refname::Error;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        git_validate::refname(value.as_bytes().as_bstr())?;
        Ok(FullName(value.into()))
    }
}

impl TryFrom<&BStr> for FullName {
    type Error = git_validate::refname::Error;

    fn try_from(value: &BStr) -> Result<Self, Self::Error> {
        Ok(FullName(git_validate::refname(value)?.into()))
    }
}

impl TryFrom<BString> for FullName {
    type Error = git_validate::refname::Error;

    fn try_from(value: BString) -> Result<Self, Self::Error> {
        git_validate::refname(value.as_ref())?;
        Ok(FullName(value))
    }
}

impl From<FullName> for BString {
    fn from(name: FullName) -> Self {
        name.0
    }
}

impl<'a> From<FullNameRef<'a>> for &'a BStr {
    fn from(name: FullNameRef<'a>) -> Self {
        name.0
    }
}

impl<'a> From<crate::FullNameRef<'a>> for FullName {
    fn from(value: crate::FullNameRef<'a>) -> Self {
        FullName(value.as_bstr().into())
    }
}

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

impl FullName {
    /// Interpret this fully qualified reference name as partial name.
    pub fn to_partial(&self) -> crate::PartialNameRef<'_> {
        crate::PartialNameRef(self.0.as_bstr().into())
    }

    /// Interpret this fully qualified reference as shared full name
    pub fn to_ref(&self) -> crate::FullNameRef<'_> {
        crate::FullNameRef(self.0.as_bstr())
    }

    /// Convert this name into the relative path, lossily, identifying the reference location relative to a repository
    pub fn to_path(&self) -> &Path {
        git_features::path::from_byte_slice_or_panic_on_windows(&self.0)
    }

    /// Dissolve this instance and return the buffer.
    pub fn into_inner(self) -> BString {
        self.0
    }

    /// Return ourselves as byte string which is a valid refname
    pub fn as_bstr(&self) -> &BStr {
        self.0.as_bstr()
    }

    /// Modify ourself so that we use `namespace` as prefix, if it is not yet in the `namespace`
    pub fn prefix_namespace(&mut self, namespace: &Namespace) -> &mut Self {
        if !self.0.starts_with_str(&namespace.0) {
            self.0.insert_str(0, &namespace.0);
        }
        self
    }

    /// Strip the given `namespace` off the beginning of this name, if it is in this namespace.
    pub fn strip_namespace(&mut self, namespace: &Namespace) -> &mut Self {
        if self.0.starts_with_str(&namespace.0) {
            let prev_len = self.0.len();
            self.0.copy_within(namespace.0.len().., 0);
            self.0.resize(prev_len - namespace.0.len(), 0);
        }
        self
    }

    /// Strip well-known prefixes from the name and return it.
    ///
    /// If there is no such prefix, the original name is returned.
    pub fn shorten(&self) -> &BStr {
        self.to_ref().shorten()
    }

    /// Classify this name, or return `None` if it's unclassified.
    pub fn category(&self) -> Option<crate::Category> {
        self.to_ref().category()
    }
}

impl<'a> FullNameRef<'a> {
    /// Create an owned copy of ourself
    pub fn to_owned(&self) -> FullName {
        FullName(self.0.to_owned())
    }

    /// Return the file name portion of a full name, for instance `main` if the
    /// full name was `refs/heads/main`.
    pub fn file_name(&self) -> &BStr {
        self.0.rsplitn(2, |b| *b == b'/').next().expect("valid ref").as_bstr()
    }
}