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::ref_cast_custom;
40use ref_cast::RefCastCustom;
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)]
99pub struct WorkspaceNameBuf(String);
100
101/// Borrowed workspace name.
102///
103/// Use `.as_str()` or `.as_symbol()` for displaying. Other than that, this can
104/// be considered an immutable `str`.
105#[derive(ContentHash, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom)]
106#[repr(transparent)]
107pub struct WorkspaceName(str);
108
109macro_rules! impl_partial_eq {
110    ($borrowed_ty:ty, $lhs:ty, $rhs:ty) => {
111        impl PartialEq<$rhs> for $lhs {
112            fn eq(&self, other: &$rhs) -> bool {
113                <$borrowed_ty as PartialEq>::eq(self, other)
114            }
115        }
116
117        impl PartialEq<$lhs> for $rhs {
118            fn eq(&self, other: &$lhs) -> bool {
119                <$borrowed_ty as PartialEq>::eq(self, other)
120            }
121        }
122    };
123}
124
125macro_rules! impl_partial_eq_str {
126    ($borrowed_ty:ty, $lhs:ty, $rhs:ty) => {
127        impl PartialEq<$rhs> for $lhs {
128            fn eq(&self, other: &$rhs) -> bool {
129                <$borrowed_ty as PartialEq>::eq(self, other.as_ref())
130            }
131        }
132
133        impl PartialEq<$lhs> for $rhs {
134            fn eq(&self, other: &$lhs) -> bool {
135                <$borrowed_ty as PartialEq>::eq(self.as_ref(), other)
136            }
137        }
138    };
139}
140
141macro_rules! impl_name_type {
142    ($owned_ty:ident, $borrowed_ty:ident) => {
143        impl $owned_ty {
144            /// Consumes this and returns the underlying string.
145            pub fn into_string(self) -> String {
146                self.0
147            }
148        }
149
150        impl $borrowed_ty {
151            /// Wraps string name.
152            #[ref_cast_custom]
153            pub const fn new(name: &str) -> &Self;
154
155            /// Returns the underlying string.
156            pub const fn as_str(&self) -> &str {
157                &self.0
158            }
159
160            /// Converts to symbol for displaying.
161            pub fn as_symbol(&self) -> &RefSymbol {
162                RefSymbol::new(&self.0)
163            }
164        }
165
166        // Owned type can be constructed from (weakly-typed) string:
167
168        impl From<String> for $owned_ty {
169            fn from(value: String) -> Self {
170                $owned_ty(value)
171            }
172        }
173
174        impl From<&String> for $owned_ty {
175            fn from(value: &String) -> Self {
176                $owned_ty(value.clone())
177            }
178        }
179
180        impl From<&str> for $owned_ty {
181            fn from(value: &str) -> Self {
182                $owned_ty(value.to_owned())
183            }
184        }
185
186        // Owned type can be constructed from borrowed type:
187
188        impl From<&$owned_ty> for $owned_ty {
189            fn from(value: &$owned_ty) -> Self {
190                value.clone()
191            }
192        }
193
194        impl From<&$borrowed_ty> for $owned_ty {
195            fn from(value: &$borrowed_ty) -> Self {
196                value.to_owned()
197            }
198        }
199
200        // Borrowed type can be constructed from (weakly-typed) string:
201
202        impl AsRef<$borrowed_ty> for String {
203            fn as_ref(&self) -> &$borrowed_ty {
204                $borrowed_ty::new(self)
205            }
206        }
207
208        impl AsRef<$borrowed_ty> for str {
209            fn as_ref(&self) -> &$borrowed_ty {
210                $borrowed_ty::new(self)
211            }
212        }
213
214        // Types can be converted to (weakly-typed) string:
215
216        impl From<$owned_ty> for String {
217            fn from(value: $owned_ty) -> Self {
218                value.0
219            }
220        }
221
222        impl From<&$owned_ty> for String {
223            fn from(value: &$owned_ty) -> Self {
224                value.0.clone()
225            }
226        }
227
228        impl From<&$borrowed_ty> for String {
229            fn from(value: &$borrowed_ty) -> Self {
230                value.0.to_owned()
231            }
232        }
233
234        impl AsRef<str> for $owned_ty {
235            fn as_ref(&self) -> &str {
236                self.as_str()
237            }
238        }
239
240        impl AsRef<str> for $borrowed_ty {
241            fn as_ref(&self) -> &str {
242                self.as_str()
243            }
244        }
245
246        // Types can be converted to borrowed type, and back to owned type:
247
248        impl AsRef<$borrowed_ty> for $owned_ty {
249            fn as_ref(&self) -> &$borrowed_ty {
250                self
251            }
252        }
253
254        impl AsRef<$borrowed_ty> for $borrowed_ty {
255            fn as_ref(&self) -> &$borrowed_ty {
256                self
257            }
258        }
259
260        impl Borrow<$borrowed_ty> for $owned_ty {
261            fn borrow(&self) -> &$borrowed_ty {
262                self
263            }
264        }
265
266        impl Deref for $owned_ty {
267            type Target = $borrowed_ty;
268
269            fn deref(&self) -> &Self::Target {
270                $borrowed_ty::new(&self.0)
271            }
272        }
273
274        impl ToOwned for $borrowed_ty {
275            type Owned = $owned_ty;
276
277            fn to_owned(&self) -> Self::Owned {
278                $owned_ty(self.0.to_owned())
279            }
280        }
281
282        // Owned and borrowed types can be compared:
283        impl_partial_eq!($borrowed_ty, $owned_ty, $borrowed_ty);
284        impl_partial_eq!($borrowed_ty, $owned_ty, &$borrowed_ty);
285
286        // Types can be compared with (weakly-typed) string:
287        impl_partial_eq_str!($borrowed_ty, $owned_ty, str);
288        impl_partial_eq_str!($borrowed_ty, $owned_ty, &str);
289        impl_partial_eq_str!($borrowed_ty, $owned_ty, String);
290        impl_partial_eq_str!($borrowed_ty, $borrowed_ty, str);
291        impl_partial_eq_str!($borrowed_ty, $borrowed_ty, &str);
292        impl_partial_eq_str!($borrowed_ty, $borrowed_ty, String);
293        impl_partial_eq_str!($borrowed_ty, &$borrowed_ty, str);
294        impl_partial_eq_str!($borrowed_ty, &$borrowed_ty, String);
295    };
296}
297
298impl_name_type!(GitRefNameBuf, GitRefName);
299// TODO: split RefName into BookmarkName and TagName? That will make sense at
300// repo/view API surface, but we'll need generic RemoteRefSymbol type, etc.
301impl_name_type!(RefNameBuf, RefName);
302impl_name_type!(RemoteNameBuf, RemoteName);
303impl_name_type!(WorkspaceNameBuf, WorkspaceName);
304
305impl RefName {
306    /// Constructs a remote symbol with this local name.
307    pub fn to_remote_symbol<'a>(&'a self, remote: &'a RemoteName) -> RemoteRefSymbol<'a> {
308        RemoteRefSymbol { name: self, remote }
309    }
310}
311
312impl WorkspaceName {
313    /// Default workspace name.
314    pub const DEFAULT: &WorkspaceName = WorkspaceName::new("default");
315}
316
317/// Symbol for displaying.
318///
319/// This type can be displayed with quoting and escaping if necessary.
320#[derive(Debug, RefCastCustom)]
321#[repr(transparent)]
322pub struct RefSymbol(str);
323
324impl RefSymbol {
325    /// Wraps string name.
326    #[ref_cast_custom]
327    const fn new(name: &str) -> &Self;
328}
329
330impl Display for RefSymbol {
331    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        f.pad(&revset::format_symbol(&self.0))
333    }
334}
335
336/// Owned remote bookmark or tag name.
337///
338/// This type can be displayed in `{name}@{remote}` form, with quoting and
339/// escaping if necessary.
340#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
341pub struct RemoteRefSymbolBuf {
342    /// Local name.
343    pub name: RefNameBuf,
344    /// Remote name.
345    pub remote: RemoteNameBuf,
346}
347
348impl RemoteRefSymbolBuf {
349    /// Converts to reference type.
350    pub fn as_ref(&self) -> RemoteRefSymbol<'_> {
351        RemoteRefSymbol {
352            name: &self.name,
353            remote: &self.remote,
354        }
355    }
356}
357
358/// Borrowed remote bookmark or tag name.
359///
360/// This type can be displayed in `{name}@{remote}` form, with quoting and
361/// escaping if necessary.
362#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
363pub struct RemoteRefSymbol<'a> {
364    /// Local name.
365    pub name: &'a RefName,
366    /// Remote name.
367    pub remote: &'a RemoteName,
368}
369
370impl RemoteRefSymbol<'_> {
371    /// Converts to owned type.
372    pub fn to_owned(self) -> RemoteRefSymbolBuf {
373        RemoteRefSymbolBuf {
374            name: self.name.to_owned(),
375            remote: self.remote.to_owned(),
376        }
377    }
378}
379
380impl From<RemoteRefSymbol<'_>> for RemoteRefSymbolBuf {
381    fn from(value: RemoteRefSymbol<'_>) -> Self {
382        value.to_owned()
383    }
384}
385
386impl PartialEq<RemoteRefSymbol<'_>> for RemoteRefSymbolBuf {
387    fn eq(&self, other: &RemoteRefSymbol) -> bool {
388        self.as_ref() == *other
389    }
390}
391
392impl PartialEq<RemoteRefSymbol<'_>> for &RemoteRefSymbolBuf {
393    fn eq(&self, other: &RemoteRefSymbol) -> bool {
394        self.as_ref() == *other
395    }
396}
397
398impl PartialEq<RemoteRefSymbolBuf> for RemoteRefSymbol<'_> {
399    fn eq(&self, other: &RemoteRefSymbolBuf) -> bool {
400        *self == other.as_ref()
401    }
402}
403
404impl PartialEq<&RemoteRefSymbolBuf> for RemoteRefSymbol<'_> {
405    fn eq(&self, other: &&RemoteRefSymbolBuf) -> bool {
406        *self == other.as_ref()
407    }
408}
409
410impl Display for RemoteRefSymbolBuf {
411    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412        Display::fmt(&self.as_ref(), f)
413    }
414}
415
416impl Display for RemoteRefSymbol<'_> {
417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418        let RemoteRefSymbol { name, remote } = self;
419        f.pad(&revset::format_remote_symbol(&name.0, &remote.0))
420    }
421}