uv_git_types/
reference.rs1use std::borrow::Cow;
2use std::fmt::Display;
3use std::str;
4
5use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode};
6
7use crate::GitOid;
8
9const GIT_REFERENCE_ENCODE_SET: &AsciiSet = &NON_ALPHANUMERIC
14 .remove(b'/')
15 .remove(b'-')
16 .remove(b'.')
17 .remove(b'_')
18 .remove(b'~');
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub enum GitReference {
23 Commit(GitOid),
25 Branch(String),
27 Tag(String),
29 BranchOrTag(String),
31 BranchOrTagOrCommit(String),
33 NamedRef(String),
35 DefaultBranch,
37}
38
39impl GitReference {
40 pub fn from_rev(rev: String) -> Self {
43 if rev.starts_with("refs/") {
44 Self::NamedRef(rev)
45 } else if let Ok(commit) = rev.parse() {
46 Self::Commit(commit)
47 } else if looks_like_commit_hash(&rev) {
48 Self::BranchOrTagOrCommit(rev)
49 } else {
50 Self::BranchOrTag(rev)
51 }
52 }
53
54 pub fn as_str(&self) -> Option<&str> {
56 match self {
57 Self::Commit(commit) => Some(commit.as_str()),
58 Self::Tag(rev) => Some(rev),
59 Self::Branch(rev) => Some(rev),
60 Self::BranchOrTag(rev) => Some(rev),
61 Self::BranchOrTagOrCommit(rev) => Some(rev),
62 Self::NamedRef(rev) => Some(rev),
63 Self::DefaultBranch => None,
64 }
65 }
66
67 pub fn as_rev(&self) -> &str {
69 match self {
70 Self::Commit(commit) => commit.as_str(),
71 Self::Tag(rev) => rev,
72 Self::Branch(rev) => rev,
73 Self::BranchOrTag(rev) => rev,
74 Self::BranchOrTagOrCommit(rev) => rev,
75 Self::NamedRef(rev) => rev,
76 Self::DefaultBranch => "HEAD",
77 }
78 }
79
80 pub fn as_url_rev(&self) -> Option<Cow<'_, str>> {
82 self.as_str().map(Self::encode_rev)
83 }
84
85 pub fn as_commit(&self) -> Option<GitOid> {
87 match self {
88 Self::Commit(commit) => Some(*commit),
89 Self::Branch(_)
90 | Self::Tag(_)
91 | Self::BranchOrTag(_)
92 | Self::BranchOrTagOrCommit(_)
93 | Self::NamedRef(_)
94 | Self::DefaultBranch => None,
95 }
96 }
97
98 fn encode_rev(rev: &str) -> Cow<'_, str> {
100 utf8_percent_encode(rev, GIT_REFERENCE_ENCODE_SET).into()
101 }
102
103 pub fn kind_str(&self) -> &str {
105 match self {
106 Self::Commit(_) => "commit",
107 Self::Branch(_) => "branch",
108 Self::Tag(_) => "tag",
109 Self::BranchOrTag(_) => "branch or tag",
110 Self::BranchOrTagOrCommit(_) => "branch, tag, or commit",
111 Self::NamedRef(_) => "ref",
112 Self::DefaultBranch => "default branch",
113 }
114 }
115}
116
117impl Display for GitReference {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(f, "{}", self.as_str().unwrap_or("HEAD"))
120 }
121}
122
123fn looks_like_commit_hash(rev: &str) -> bool {
125 rev.len() >= 7 && rev.chars().all(|ch| ch.is_ascii_hexdigit())
126}