1use std::cmp::Ordering;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum SourceKind {
6 Git(GitReference),
8 Path,
10 Registry,
12 SparseRegistry,
14 LocalRegistry,
16 Directory,
18 Builtin,
20}
21
22impl std::hash::Hash for SourceKind {
27 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
28 core::mem::discriminant(self).hash(state);
29 if let SourceKind::Git(git) = self {
30 git.hash(state);
31 }
32 }
33}
34
35impl SourceKind {
36 pub fn protocol(&self) -> Option<&str> {
37 match self {
38 SourceKind::Path => Some("path"),
39 SourceKind::Git(_) => Some("git"),
40 SourceKind::Registry => Some("registry"),
41 SourceKind::SparseRegistry => None,
43 SourceKind::LocalRegistry => Some("local-registry"),
44 SourceKind::Directory => Some("directory"),
45 SourceKind::Builtin => None,
47 }
48 }
49}
50
51impl Ord for SourceKind {
55 fn cmp(&self, other: &SourceKind) -> Ordering {
56 match (self, other) {
57 (SourceKind::Path, SourceKind::Path) => Ordering::Equal,
58 (SourceKind::Path, _) => Ordering::Less,
59 (_, SourceKind::Path) => Ordering::Greater,
60
61 (SourceKind::Registry, SourceKind::Registry) => Ordering::Equal,
62 (SourceKind::Registry, _) => Ordering::Less,
63 (_, SourceKind::Registry) => Ordering::Greater,
64
65 (SourceKind::SparseRegistry, SourceKind::SparseRegistry) => Ordering::Equal,
66 (SourceKind::SparseRegistry, _) => Ordering::Less,
67 (_, SourceKind::SparseRegistry) => Ordering::Greater,
68
69 (SourceKind::LocalRegistry, SourceKind::LocalRegistry) => Ordering::Equal,
70 (SourceKind::LocalRegistry, _) => Ordering::Less,
71 (_, SourceKind::LocalRegistry) => Ordering::Greater,
72
73 (SourceKind::Directory, SourceKind::Directory) => Ordering::Equal,
74 (SourceKind::Directory, _) => Ordering::Less,
75 (_, SourceKind::Directory) => Ordering::Greater,
76
77 (SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b),
78 (SourceKind::Git(_), _) => Ordering::Less,
79 (_, SourceKind::Git(_)) => Ordering::Greater,
80
81 (SourceKind::Builtin, SourceKind::Builtin) => Ordering::Equal,
82 }
83 }
84}
85
86impl PartialOrd for SourceKind {
88 fn partial_cmp(&self, other: &SourceKind) -> Option<Ordering> {
89 Some(self.cmp(other))
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
95pub enum GitReference {
96 Tag(String),
98 Branch(String),
100 Rev(String),
103 DefaultBranch,
105}
106
107impl GitReference {
108 pub fn from_query(
109 query_pairs: impl Iterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
110 ) -> Self {
111 let mut reference = GitReference::DefaultBranch;
112 for (k, v) in query_pairs {
113 let v = v.as_ref();
114 match k.as_ref() {
115 "branch" | "ref" => reference = GitReference::Branch(v.to_owned()),
117
118 "rev" => reference = GitReference::Rev(v.to_owned()),
119 "tag" => reference = GitReference::Tag(v.to_owned()),
120 _ => {}
121 }
122 }
123 reference
124 }
125
126 pub fn pretty_ref(&self, url_encoded: bool) -> Option<PrettyRef<'_>> {
129 match self {
130 GitReference::DefaultBranch => None,
131 _ => Some(PrettyRef {
132 inner: self,
133 url_encoded,
134 }),
135 }
136 }
137}
138
139pub struct PrettyRef<'a> {
141 inner: &'a GitReference,
142 url_encoded: bool,
143}
144
145impl<'a> std::fmt::Display for PrettyRef<'a> {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 let value: &str;
148 match self.inner {
149 GitReference::Branch(s) => {
150 write!(f, "branch=")?;
151 value = s;
152 }
153 GitReference::Tag(s) => {
154 write!(f, "tag=")?;
155 value = s;
156 }
157 GitReference::Rev(s) => {
158 write!(f, "rev=")?;
159 value = s;
160 }
161 GitReference::DefaultBranch => unreachable!(),
162 }
163 if self.url_encoded {
164 for value in url::form_urlencoded::byte_serialize(value.as_bytes()) {
165 write!(f, "{value}")?;
166 }
167 } else {
168 write!(f, "{value}")?;
169 }
170 Ok(())
171 }
172}