uv_git_types/
reference.rs1use std::fmt::Display;
2use std::str;
3
4use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode};
5
6const GIT_REFERENCE_ENCODE_SET: &AsciiSet = &NON_ALPHANUMERIC
11 .remove(b'/')
12 .remove(b'-')
13 .remove(b'.')
14 .remove(b'_')
15 .remove(b'~');
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub enum GitReference {
20 Branch(String),
22 Tag(String),
24 BranchOrTag(String),
26 BranchOrTagOrCommit(String),
28 NamedRef(String),
30 DefaultBranch,
32}
33
34impl GitReference {
35 pub fn from_rev(rev: String) -> Self {
38 if rev.starts_with("refs/") {
39 Self::NamedRef(rev)
40 } else if looks_like_commit_hash(&rev) {
41 Self::BranchOrTagOrCommit(rev)
42 } else {
43 Self::BranchOrTag(rev)
44 }
45 }
46
47 pub fn as_str(&self) -> Option<&str> {
49 match self {
50 Self::Tag(rev) => Some(rev),
51 Self::Branch(rev) => Some(rev),
52 Self::BranchOrTag(rev) => Some(rev),
53 Self::BranchOrTagOrCommit(rev) => Some(rev),
54 Self::NamedRef(rev) => Some(rev),
55 Self::DefaultBranch => None,
56 }
57 }
58
59 pub fn as_rev(&self) -> &str {
61 match self {
62 Self::Tag(rev) => rev,
63 Self::Branch(rev) => rev,
64 Self::BranchOrTag(rev) => rev,
65 Self::BranchOrTagOrCommit(rev) => rev,
66 Self::NamedRef(rev) => rev,
67 Self::DefaultBranch => "HEAD",
68 }
69 }
70
71 pub fn as_url_rev(&self) -> Option<String> {
73 self.as_str().map(Self::encode_rev)
74 }
75
76 pub fn encode_rev(rev: &str) -> String {
78 utf8_percent_encode(rev, GIT_REFERENCE_ENCODE_SET).to_string()
79 }
80
81 pub fn kind_str(&self) -> &str {
83 match self {
84 Self::Branch(_) => "branch",
85 Self::Tag(_) => "tag",
86 Self::BranchOrTag(_) => "branch or tag",
87 Self::BranchOrTagOrCommit(_) => "branch, tag, or commit",
88 Self::NamedRef(_) => "ref",
89 Self::DefaultBranch => "default branch",
90 }
91 }
92}
93
94impl Display for GitReference {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 write!(f, "{}", self.as_str().unwrap_or("HEAD"))
97 }
98}
99
100fn looks_like_commit_hash(rev: &str) -> bool {
102 rev.len() >= 7 && rev.chars().all(|ch| ch.is_ascii_hexdigit())
103}