jj_lib/
ref_name.rs

1// Copyright 2025 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Name types for commit references.
16//!
17//! Name types can be constructed from a string:
18//! ```
19//! # use jj_lib::ref_name::*;
20//! let _: RefNameBuf = "main".into();
21//! let _: &RemoteName = "origin".as_ref();
22//! ```
23//!
24//! However, they cannot be converted to other name types:
25//! ```compile_fail
26//! # use jj_lib::ref_name::*;
27//! let _: RefNameBuf = RemoteName::new("origin").into();
28//! ```
29//! ```compile_fail
30//! # use jj_lib::ref_name::*;
31//! let _: &RemoteName = RefName::new("main").as_ref();
32//! ```
33
34use std::borrow::Borrow;
35use std::fmt;
36use std::fmt::Display;
37use std::ops::Deref;
38
39use ref_cast::RefCastCustom;
40use ref_cast::ref_cast_custom;
41
42use crate::content_hash::ContentHash;
43use crate::revset;
44
45/// Owned Git ref name in fully-qualified form (e.g. `refs/heads/main`.)
46///
47/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
48/// be considered an immutable `String`.
49// Eq, Hash, and Ord must be compatible with GitRefName.
50#[derive(Clone, ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
51pub struct GitRefNameBuf(String);
52
53/// Borrowed Git ref name in fully-qualified form (e.g. `refs/heads/main`.)
54///
55/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
56/// be considered an immutable `str`.
57#[derive(ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom)]
58#[repr(transparent)]
59pub struct GitRefName(str);
60
61/// Owned local (or local part of remote) bookmark or tag name.
62///
63/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
64/// be considered an immutable `String`.
65// Eq, Hash, and Ord must be compatible with RefName.
66#[derive(Clone, ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
67pub struct RefNameBuf(String);
68
69/// Borrowed local (or local part of remote) bookmark or tag name.
70///
71/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
72/// be considered an immutable `str`.
73#[derive(ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom)]
74#[repr(transparent)]
75pub struct RefName(str);
76
77/// Owned remote name.
78///
79/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
80/// be considered an immutable `String`.
81// Eq, Hash, and Ord must be compatible with RemoteName.
82#[derive(Clone, ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
83pub struct RemoteNameBuf(String);
84
85/// Borrowed remote name.
86///
87/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
88/// be considered an immutable `str`.
89#[derive(ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom)]
90#[repr(transparent)]
91pub struct RemoteName(str);
92
93/// Owned workspace name.
94///
95/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
96/// be considered an immutable `String`.
97// Eq, Hash, and Ord must be compatible with WorkspaceName.
98#[derive(Clone, ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Serialize)]
99#[serde(transparent)]
100pub struct WorkspaceNameBuf(String);
101
102/// Borrowed workspace name.
103///
104/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
105/// be considered an immutable `str`.
106#[derive(
107    ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom, serde::Serialize,
108)]
109#[serde(transparent)]
110#[repr(transparent)]
111pub struct WorkspaceName(str);
112
113macro_rules! impl_partial_eq {
114    ($borrowed_ty:ty, $lhs:ty, $rhs:ty) => {
115        impl PartialEq<$rhs> for $lhs {
116            fn eq(&self, other: &$rhs) -> bool {
117                <$borrowed_ty as PartialEq>::eq(self, other)
118            }
119        }
120
121        impl PartialEq<$lhs> for $rhs {
122            fn eq(&self, other: &$lhs) -> bool {
123                <$borrowed_ty as PartialEq>::eq(self, other)
124            }
125        }
126    };
127}
128
129macro_rules! impl_partial_eq_str {
130    ($borrowed_ty:ty, $lhs:ty, $rhs:ty) => {
131        impl PartialEq<$rhs> for $lhs {
132            fn eq(&self, other: &$rhs) -> bool {
133                <$borrowed_ty as PartialEq>::eq(self, other.as_ref())
134            }
135        }
136
137        impl PartialEq<$lhs> for $rhs {
138            fn eq(&self, other: &$lhs) -> bool {
139                <$borrowed_ty as PartialEq>::eq(self.as_ref(), other)
140            }
141        }
142    };
143}
144
145macro_rules! impl_name_type {
146    ($owned_ty:ident, $borrowed_ty:ident) => {
147        impl $owned_ty {
148            /// Consumes this and returns the underlying string.
149            pub fn into_string(self) -> String {
150                self.0
151            }
152        }
153
154        impl $borrowed_ty {
155            /// Wraps string name.
156            #[ref_cast_custom]
157            pub const fn new(name: &str) -> &Self;
158
159            /// Returns the underlying string.
160            pub const fn as_str(&self) -> &str {
161                &self.0
162            }
163
164            /// Converts to symbol for displaying.
165            pub fn as_symbol(&self) -> &RefSymbol {
166                RefSymbol::new(&self.0)
167            }
168        }
169
170        // Owned type can be constructed from (weakly-typed) string:
171
172        impl From<String> for $owned_ty {
173            fn from(value: String) -> Self {
174                $owned_ty(value)
175            }
176        }
177
178        impl From<&String> for $owned_ty {
179            fn from(value: &String) -> Self {
180                $owned_ty(value.clone())
181            }
182        }
183
184        impl From<&str> for $owned_ty {
185            fn from(value: &str) -> Self {
186                $owned_ty(value.to_owned())
187            }
188        }
189
190        // Owned type can be constructed from borrowed type:
191
192        impl From<&$owned_ty> for $owned_ty {
193            fn from(value: &$owned_ty) -> Self {
194                value.clone()
195            }
196        }
197
198        impl From<&$borrowed_ty> for $owned_ty {
199            fn from(value: &$borrowed_ty) -> Self {
200                value.to_owned()
201            }
202        }
203
204        // Borrowed type can be constructed from (weakly-typed) string:
205
206        impl AsRef<$borrowed_ty> for String {
207            fn as_ref(&self) -> &$borrowed_ty {
208                $borrowed_ty::new(self)
209            }
210        }
211
212        impl AsRef<$borrowed_ty> for str {
213            fn as_ref(&self) -> &$borrowed_ty {
214                $borrowed_ty::new(self)
215            }
216        }
217
218        // Types can be converted to (weakly-typed) string:
219
220        impl From<$owned_ty> for String {
221            fn from(value: $owned_ty) -> Self {
222                value.0
223            }
224        }
225
226        impl From<&$owned_ty> for String {
227            fn from(value: &$owned_ty) -> Self {
228                value.0.clone()
229            }
230        }
231
232        impl From<&$borrowed_ty> for String {
233            fn from(value: &$borrowed_ty) -> Self {
234                value.0.to_owned()
235            }
236        }
237
238        impl AsRef<str> for $owned_ty {
239            fn as_ref(&self) -> &str {
240                self.as_str()
241            }
242        }
243
244        impl AsRef<str> for $borrowed_ty {
245            fn as_ref(&self) -> &str {
246                self.as_str()
247            }
248        }
249
250        // Types can be converted to borrowed type, and back to owned type:
251
252        impl AsRef<$borrowed_ty> for $owned_ty {
253            fn as_ref(&self) -> &$borrowed_ty {
254                self
255            }
256        }
257
258        impl AsRef<$borrowed_ty> for $borrowed_ty {
259            fn as_ref(&self) -> &$borrowed_ty {
260                self
261            }
262        }
263
264        impl Borrow<$borrowed_ty> for $owned_ty {
265            fn borrow(&self) -> &$borrowed_ty {
266                self
267            }
268        }
269
270        impl Deref for $owned_ty {
271            type Target = $borrowed_ty;
272
273            fn deref(&self) -> &Self::Target {
274                $borrowed_ty::new(&self.0)
275            }
276        }
277
278        impl ToOwned for $borrowed_ty {
279            type Owned = $owned_ty;
280
281            fn to_owned(&self) -> Self::Owned {
282                $owned_ty(self.0.to_owned())
283            }
284        }
285
286        // Owned and borrowed types can be compared:
287        impl_partial_eq!($borrowed_ty, $owned_ty, $borrowed_ty);
288        impl_partial_eq!($borrowed_ty, $owned_ty, &$borrowed_ty);
289
290        // Types can be compared with (weakly-typed) string:
291        impl_partial_eq_str!($borrowed_ty, $owned_ty, str);
292        impl_partial_eq_str!($borrowed_ty, $owned_ty, &str);
293        impl_partial_eq_str!($borrowed_ty, $owned_ty, String);
294        impl_partial_eq_str!($borrowed_ty, $borrowed_ty, str);
295        impl_partial_eq_str!($borrowed_ty, $borrowed_ty, &str);
296        impl_partial_eq_str!($borrowed_ty, $borrowed_ty, String);
297        impl_partial_eq_str!($borrowed_ty, &$borrowed_ty, str);
298        impl_partial_eq_str!($borrowed_ty, &$borrowed_ty, String);
299    };
300}
301
302impl_name_type!(GitRefNameBuf, GitRefName);
303// TODO: split RefName into BookmarkName and TagName? That will make sense at
304// repo/view API surface, but we'll need generic RemoteRefSymbol type, etc.
305impl_name_type!(RefNameBuf, RefName);
306impl_name_type!(RemoteNameBuf, RemoteName);
307impl_name_type!(WorkspaceNameBuf, WorkspaceName);
308
309impl RefName {
310    /// Constructs a remote symbol with this local name.
311    pub fn to_remote_symbol<'a>(&'a self, remote: &'a RemoteName) -> RemoteRefSymbol<'a> {
312        RemoteRefSymbol { name: self, remote }
313    }
314}
315
316impl WorkspaceName {
317    /// Default workspace name.
318    pub const DEFAULT: &Self = Self::new("default");
319}
320
321/// Symbol for displaying.
322///
323/// This type can be displayed with quoting and escaping if necessary.
324#[derive(Debug, RefCastCustom)]
325#[repr(transparent)]
326pub struct RefSymbol(str);
327
328impl RefSymbol {
329    /// Wraps string name.
330    #[ref_cast_custom]
331    const fn new(name: &str) -> &Self;
332}
333
334impl Display for RefSymbol {
335    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336        f.pad(&revset::format_symbol(&self.0))
337    }
338}
339
340/// Owned remote bookmark or tag name.
341///
342/// This type can be displayed in `{name}@{remote}` form, with quoting and
343/// escaping if necessary.
344#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
345pub struct RemoteRefSymbolBuf {
346    /// Local name.
347    pub name: RefNameBuf,
348    /// Remote name.
349    pub remote: RemoteNameBuf,
350}
351
352impl RemoteRefSymbolBuf {
353    /// Converts to reference type.
354    pub fn as_ref(&self) -> RemoteRefSymbol<'_> {
355        RemoteRefSymbol {
356            name: &self.name,
357            remote: &self.remote,
358        }
359    }
360}
361
362/// Borrowed remote bookmark or tag name.
363///
364/// This type can be displayed in `{name}@{remote}` form, with quoting and
365/// escaping if necessary.
366#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
367pub struct RemoteRefSymbol<'a> {
368    /// Local name.
369    pub name: &'a RefName,
370    /// Remote name.
371    pub remote: &'a RemoteName,
372}
373
374impl RemoteRefSymbol<'_> {
375    /// Converts to owned type.
376    pub fn to_owned(self) -> RemoteRefSymbolBuf {
377        RemoteRefSymbolBuf {
378            name: self.name.to_owned(),
379            remote: self.remote.to_owned(),
380        }
381    }
382}
383
384impl From<RemoteRefSymbol<'_>> for RemoteRefSymbolBuf {
385    fn from(value: RemoteRefSymbol<'_>) -> Self {
386        value.to_owned()
387    }
388}
389
390impl PartialEq<RemoteRefSymbol<'_>> for RemoteRefSymbolBuf {
391    fn eq(&self, other: &RemoteRefSymbol) -> bool {
392        self.as_ref() == *other
393    }
394}
395
396impl PartialEq<RemoteRefSymbol<'_>> for &RemoteRefSymbolBuf {
397    fn eq(&self, other: &RemoteRefSymbol) -> bool {
398        self.as_ref() == *other
399    }
400}
401
402impl PartialEq<RemoteRefSymbolBuf> for RemoteRefSymbol<'_> {
403    fn eq(&self, other: &RemoteRefSymbolBuf) -> bool {
404        *self == other.as_ref()
405    }
406}
407
408impl PartialEq<&RemoteRefSymbolBuf> for RemoteRefSymbol<'_> {
409    fn eq(&self, other: &&RemoteRefSymbolBuf) -> bool {
410        *self == other.as_ref()
411    }
412}
413
414impl Display for RemoteRefSymbolBuf {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        Display::fmt(&self.as_ref(), f)
417    }
418}
419
420impl Display for RemoteRefSymbol<'_> {
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        let RemoteRefSymbol { name, remote } = self;
423        f.pad(&revset::format_remote_symbol(&name.0, &remote.0))
424    }
425}