1use std::{convert, convert::Infallible, ffi::OsStr, path::Path};
2
3use gix_object::bstr::{BStr, BString, ByteSlice, ByteVec};
4
5use crate::{Category, FullName, FullNameRef, PartialName, PartialNameRef};
6
7pub type Error = gix_validate::reference::name::Error;
9
10impl Category<'_> {
11 pub fn prefix(&self) -> &BStr {
14 match self {
15 Category::Tag => b"refs/tags/".as_bstr(),
16 Category::LocalBranch => b"refs/heads/".as_bstr(),
17 Category::RemoteBranch => b"refs/remotes/".as_bstr(),
18 Category::Note => b"refs/notes/".as_bstr(),
19 Category::MainPseudoRef => b"main-worktree/".as_bstr(),
20 Category::MainRef => b"main-worktree/refs/".as_bstr(),
21 Category::PseudoRef => b"".as_bstr(),
22 Category::LinkedPseudoRef { .. } => b"worktrees/".as_bstr(),
23 Category::LinkedRef { .. } => b"worktrees/".as_bstr(),
24 Category::Bisect => b"refs/bisect/".as_bstr(),
25 Category::Rewritten => b"refs/rewritten/".as_bstr(),
26 Category::WorktreePrivate => b"refs/worktree/".as_bstr(),
27 }
28 }
29
30 pub fn is_worktree_private(&self) -> bool {
32 matches!(
33 self,
34 Category::MainPseudoRef
35 | Category::PseudoRef
36 | Category::LinkedPseudoRef { .. }
37 | Category::WorktreePrivate
38 | Category::Rewritten
39 | Category::Bisect
40 )
41 }
42}
43
44impl FullNameRef {
45 pub(crate) fn new_unchecked(v: &BStr) -> &Self {
46 #[allow(unsafe_code)]
48 unsafe {
49 std::mem::transmute(v)
50 }
51 }
52}
53
54impl PartialNameRef {
55 pub(crate) fn new_unchecked(v: &BStr) -> &Self {
56 #[allow(unsafe_code)]
58 unsafe {
59 std::mem::transmute(v)
60 }
61 }
62}
63
64impl PartialNameRef {
65 pub(crate) fn looks_like_full_name(&self, consider_pseudo_ref: bool) -> bool {
66 let name = self.0.as_bstr();
67 name.starts_with_str("refs/")
68 || name.starts_with(Category::MainPseudoRef.prefix())
69 || name.starts_with(Category::LinkedPseudoRef { name: "".into() }.prefix())
70 || (consider_pseudo_ref && is_pseudo_ref(name))
71 }
72 pub(crate) fn construct_full_name_ref<'buf>(
73 &self,
74 inbetween: &str,
75 buf: &'buf mut BString,
76 consider_pseudo_ref: bool,
77 ) -> &'buf FullNameRef {
78 buf.clear();
79 if !self.looks_like_full_name(consider_pseudo_ref) {
80 buf.push_str("refs/");
81 }
82 if !inbetween.is_empty() {
83 buf.push_str(inbetween);
84 buf.push_byte(b'/');
85 }
86 buf.extend_from_slice(&self.0);
87 FullNameRef::new_unchecked(buf.as_bstr())
88 }
89}
90
91impl PartialNameRef {
92 pub fn to_partial_path(&self) -> &Path {
95 gix_path::from_byte_slice(self.0.as_bstr())
96 }
97
98 pub fn as_bstr(&self) -> &BStr {
100 &self.0
101 }
102}
103
104impl PartialName {
105 pub fn join(self, component: &BStr) -> Result<Self, Error> {
107 let mut b = self.0;
108 b.push_byte(b'/');
109 b.extend(component.as_bytes());
110 gix_validate::reference::name_partial(b.as_ref())?;
111 Ok(PartialName(b))
112 }
113}
114
115impl<'a> convert::TryFrom<&'a BStr> for &'a FullNameRef {
116 type Error = Error;
117
118 fn try_from(v: &'a BStr) -> Result<Self, Self::Error> {
119 Ok(FullNameRef::new_unchecked(gix_validate::reference::name(v)?))
120 }
121}
122
123impl<'a> From<&'a FullNameRef> for &'a PartialNameRef {
124 fn from(v: &'a FullNameRef) -> Self {
125 PartialNameRef::new_unchecked(v.0.as_bstr())
126 }
127}
128
129impl<'a> convert::TryFrom<&'a OsStr> for &'a PartialNameRef {
130 type Error = Error;
131
132 fn try_from(v: &'a OsStr) -> Result<Self, Self::Error> {
133 let v = gix_path::os_str_into_bstr(v).map_err(|_| Error::InvalidByte {
134 byte: "<unknown encoding>".into(),
135 })?;
136 Ok(PartialNameRef::new_unchecked(gix_validate::reference::name_partial(
137 v.as_bstr(),
138 )?))
139 }
140}
141
142mod impls {
143 use std::borrow::Borrow;
144
145 use crate::{bstr::ByteSlice, PartialName, PartialNameRef};
146
147 impl Borrow<PartialNameRef> for PartialName {
148 #[inline]
149 fn borrow(&self) -> &PartialNameRef {
150 PartialNameRef::new_unchecked(self.0.as_bstr())
151 }
152 }
153
154 impl AsRef<PartialNameRef> for PartialName {
155 fn as_ref(&self) -> &PartialNameRef {
156 self.borrow()
157 }
158 }
159
160 impl ToOwned for PartialNameRef {
161 type Owned = PartialName;
162
163 fn to_owned(&self) -> Self::Owned {
164 PartialName(self.0.to_owned())
165 }
166 }
167}
168
169impl<'a> convert::TryFrom<&'a BString> for &'a PartialNameRef {
170 type Error = Error;
171
172 fn try_from(v: &'a BString) -> Result<Self, Self::Error> {
173 Ok(PartialNameRef::new_unchecked(gix_validate::reference::name_partial(
174 v.as_ref(),
175 )?))
176 }
177}
178
179impl<'a> convert::TryFrom<&'a BStr> for &'a PartialNameRef {
180 type Error = Error;
181
182 fn try_from(v: &'a BStr) -> Result<Self, Self::Error> {
183 Ok(PartialNameRef::new_unchecked(gix_validate::reference::name_partial(v)?))
184 }
185}
186
187impl<'a> convert::TryFrom<&'a PartialName> for &'a PartialNameRef {
188 type Error = Error;
189
190 fn try_from(v: &'a PartialName) -> Result<Self, Self::Error> {
191 Ok(PartialNameRef::new_unchecked(v.0.as_bstr()))
192 }
193}
194
195impl<'a> convert::TryFrom<&'a str> for &'a FullNameRef {
196 type Error = Error;
197
198 fn try_from(v: &'a str) -> Result<Self, Self::Error> {
199 let v = v.as_bytes().as_bstr();
200 Ok(FullNameRef::new_unchecked(gix_validate::reference::name(v)?))
201 }
202}
203
204impl<'a> convert::TryFrom<&'a str> for &'a PartialNameRef {
205 type Error = Error;
206
207 fn try_from(v: &'a str) -> Result<Self, Self::Error> {
208 let v = v.as_bytes().as_bstr();
209 Ok(PartialNameRef::new_unchecked(gix_validate::reference::name_partial(v)?))
210 }
211}
212
213impl<'a> convert::TryFrom<&'a str> for PartialName {
214 type Error = Error;
215
216 fn try_from(v: &'a str) -> Result<Self, Self::Error> {
217 let v = v.as_bytes().as_bstr();
218 Ok(PartialName(gix_validate::reference::name_partial(v)?.to_owned()))
219 }
220}
221
222#[allow(clippy::infallible_try_from)]
223impl<'a> convert::TryFrom<&'a FullName> for &'a PartialNameRef {
224 type Error = Infallible;
225
226 fn try_from(v: &'a FullName) -> Result<Self, Self::Error> {
227 Ok(v.as_ref().as_partial_name())
228 }
229}
230
231impl<'a> convert::TryFrom<&'a String> for &'a FullNameRef {
232 type Error = Error;
233
234 fn try_from(v: &'a String) -> Result<Self, Self::Error> {
235 let v = v.as_bytes().as_bstr();
236 Ok(FullNameRef::new_unchecked(gix_validate::reference::name(v)?))
237 }
238}
239
240impl<'a> convert::TryFrom<&'a String> for &'a PartialNameRef {
241 type Error = Error;
242
243 fn try_from(v: &'a String) -> Result<Self, Self::Error> {
244 let v = v.as_bytes().as_bstr();
245 Ok(PartialNameRef::new_unchecked(gix_validate::reference::name_partial(v)?))
246 }
247}
248
249impl convert::TryFrom<String> for PartialName {
250 type Error = Error;
251
252 fn try_from(v: String) -> Result<Self, Self::Error> {
253 gix_validate::reference::name_partial(v.as_bytes().as_bstr())?;
254 Ok(PartialName(v.into()))
255 }
256}
257
258impl convert::TryFrom<BString> for PartialName {
259 type Error = Error;
260
261 fn try_from(v: BString) -> Result<Self, Self::Error> {
262 gix_validate::reference::name_partial(v.as_ref())?;
263 Ok(PartialName(v))
264 }
265}
266
267impl std::fmt::Display for PartialName {
268 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 std::fmt::Display::fmt(&self.0, f)
270 }
271}
272
273impl std::fmt::Display for PartialNameRef {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 std::fmt::Display::fmt(&self.0, f)
276 }
277}
278
279pub(crate) fn is_pseudo_ref(name: &BStr) -> bool {
283 name.bytes().all(|b| b.is_ascii_uppercase() || b == b'_')
284}