1use thiserror::Error;
9
10use crate::NodeType;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct UntypedEdgeLabel(pub(crate) u64);
15
16impl From<u64> for UntypedEdgeLabel {
17 #[inline(always)]
18 fn from(n: u64) -> UntypedEdgeLabel {
19 UntypedEdgeLabel(n)
20 }
21}
22
23#[derive(Error, Debug, Clone, PartialEq, Eq, Hash)]
24pub enum EdgeTypingError {
25 #[error("{src} -> {dst} arcs cannot have labels")]
26 NodeTypes { src: NodeType, dst: NodeType },
27}
28
29impl UntypedEdgeLabel {
30 pub fn for_edge_type(
31 &self,
32 src: NodeType,
33 dst: NodeType,
34 transpose_graph: bool,
35 ) -> Result<EdgeLabel, EdgeTypingError> {
36 use crate::NodeType::*;
37
38 let (src, dst) = if transpose_graph {
39 (dst, src)
40 } else {
41 (src, dst)
42 };
43
44 match (src, dst) {
45 (Snapshot, _) => Ok(EdgeLabel::Branch(self.0.into())),
46 (Directory, _) => Ok(EdgeLabel::DirEntry(self.0.into())),
47 (Origin, Snapshot) => Ok(EdgeLabel::Visit(self.0.into())),
48 _ => Err(EdgeTypingError::NodeTypes { src, dst }),
49 }
50 }
51}
52
53impl From<EdgeLabel> for UntypedEdgeLabel {
54 #[inline(always)]
55 fn from(label: EdgeLabel) -> Self {
56 UntypedEdgeLabel(match label {
57 EdgeLabel::Branch(branch) => branch.0,
58 EdgeLabel::DirEntry(dir_entry) => dir_entry.0,
59 EdgeLabel::Visit(visit) => visit.0,
60 })
61 }
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
65pub enum EdgeLabel {
66 Branch(Branch),
68 DirEntry(DirEntry),
70 Visit(Visit),
72}
73
74macro_rules! impl_edgelabel_convert {
75 ( $variant:ident ( $inner:ty ) ) => {
76 impl From<$inner> for EdgeLabel {
77 fn from(v: $inner) -> EdgeLabel {
78 EdgeLabel::$variant(v)
79 }
80 }
81
82 impl TryFrom<EdgeLabel> for $inner {
83 type Error = ();
84
85 fn try_from(label: EdgeLabel) -> Result<$inner, Self::Error> {
86 match label {
87 EdgeLabel::$variant(v) => Ok(v),
88 _ => Err(()),
89 }
90 }
91 }
92
93 impl From<UntypedEdgeLabel> for $inner {
94 fn from(label: UntypedEdgeLabel) -> $inner {
95 <$inner>::from(label.0)
96 }
97 }
98 };
99}
100
101impl_edgelabel_convert!(Branch(Branch));
102impl_edgelabel_convert!(DirEntry(DirEntry));
103impl_edgelabel_convert!(Visit(Visit));
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
114pub enum VisitType {
115 Archive,
117 Misc,
119 Package,
121 Push,
123 Vcs,
125 VcsCheckout,
127 Unknown,
129}
130
131impl VisitType {
132 #[rustfmt::skip]
133 pub fn from_swh_type(swh_type: &str) -> Option<Self> {
134 Some(match swh_type {
135 "ftp" |
136 "tar" |
137 "tarball-directory"
138 => VisitType::Archive,
139
140 "content"
141 => VisitType::Misc,
142
143 "bioconductor" |
144 "cpan" |
145 "cran" |
146 "crates" |
147 "deb" |
148 "golang" |
149 "hackage" |
150 "hex" |
151 "opam" |
152 "nixguix" |
153 "npm" |
154 "maven" |
155 "pubdev" |
156 "puppet" |
157 "pypi" |
158 "rubygems"
159 => VisitType::Package,
160
161 "deposit"
162 => VisitType::Push,
163
164 "bzr" |
165 "cvs" |
166 "git" |
167 "hg" |
168 "svn"
169 => VisitType::Vcs,
170
171 "hg-checkout" |
172 "git-checkout" |
173 "svn-export"
174 => VisitType::VcsCheckout,
175
176 _ => return None,
177 })
178 }
179
180 pub fn to_bits(self) -> u8 {
181 use VisitType::*;
182
183 match self {
184 Unknown => 0b000,
185 Archive => 0b001,
186 Misc => 0b010,
187 Package => 0b011,
188 Push => 0b100,
189 Vcs => 0b101,
190 VcsCheckout => 0b110,
191 }
192 }
193
194 pub fn from_bits(bits: u8) -> Self {
195 use VisitType::*;
196
197 match bits {
198 0b000 => Unknown, 0b001 => Archive,
200 0b010 => Misc,
201 0b011 => Package,
202 0b100 => Push,
203 0b101 => Vcs,
204 0b110 => VcsCheckout,
205 0b111 => Unknown, 0b1000..=u8::MAX => {
207 panic!("VisitType::from_bits got value greater than 0b111");
208 }
209 }
210 }
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
214pub enum VisitStatus {
215 Full,
216 Partial,
217}
218
219#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
220pub struct Visit(pub(crate) u64);
221
222impl From<u64> for Visit {
223 #[inline(always)]
224 fn from(n: u64) -> Visit {
225 Visit(n)
226 }
227}
228
229impl Visit {
230 pub fn new(status: VisitStatus, timestamp: u64, visit_type: VisitType) -> Option<Visit> {
234 let is_full = match status {
235 VisitStatus::Full => 1u64,
236 VisitStatus::Partial => 0,
237 };
238 let reserved_bits = 0b1000u64;
239 let visit_type = u64::from(visit_type.to_bits());
240 assert!(visit_type <= 0b111);
241 timestamp.checked_shl(5).map(|shifted_timestamp| {
242 Visit(shifted_timestamp | (is_full << 4) | reserved_bits | visit_type)
243 })
244 }
245
246 #[inline(always)]
247 pub fn timestamp(&self) -> u64 {
248 self.0 >> 5
249 }
250
251 #[inline(always)]
252 pub fn status(&self) -> VisitStatus {
253 if self.0 & 0b10000 != 0 {
254 VisitStatus::Full
255 } else {
256 VisitStatus::Partial
257 }
258 }
259
260 pub fn visit_type(&self) -> VisitType {
261 VisitType::from_bits((self.0 & 0b111) as u8)
262 }
263}
264
265#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
266pub struct Branch(pub(crate) u64);
267
268impl From<u64> for Branch {
269 #[inline(always)]
270 fn from(n: u64) -> Branch {
271 Branch(n)
272 }
273}
274
275impl Branch {
276 pub fn new(label_name_id: LabelNameId) -> Option<Branch> {
280 label_name_id.0.checked_shl(3).map(Branch)
281 }
282
283 #[deprecated(since = "7.0.0", note = "filename_id was renamed label_name_id")]
284 #[inline(always)]
286 pub fn filename_id(self) -> LabelNameId {
287 self.label_name_id()
288 }
289
290 #[inline(always)]
294 pub fn label_name_id(self) -> LabelNameId {
295 LabelNameId(self.0 >> 3)
296 }
297}
298
299#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
300pub struct DirEntry(pub(crate) u64);
301
302impl From<u64> for DirEntry {
303 #[inline(always)]
304 fn from(n: u64) -> DirEntry {
305 DirEntry(n)
306 }
307}
308
309impl DirEntry {
310 pub fn new(permission: Permission, label_name_id: LabelNameId) -> Option<DirEntry> {
314 label_name_id
315 .0
316 .checked_shl(3)
317 .map(|shifted_label_name_id| DirEntry(shifted_label_name_id | (permission as u64)))
318 }
319
320 #[deprecated(since = "7.0.0", note = "filename_id was renamed label_name_id")]
321 #[inline(always)]
323 pub fn filename_id(self) -> LabelNameId {
324 self.label_name_id()
325 }
326
327 #[inline(always)]
331 pub fn label_name_id(self) -> LabelNameId {
332 LabelNameId(self.0 >> 3)
333 }
334
335 pub fn permission(self) -> Option<Permission> {
340 use Permission::*;
341 match self.0 & 0b111 {
342 0 => Some(None),
343 1 => Some(Content),
344 2 => Some(ExecutableContent),
345 3 => Some(Symlink),
346 4 => Some(Directory),
347 5 => Some(Revision),
348 _ => Option::None,
349 }
350 }
351
352 pub unsafe fn permission_unchecked(self) -> Permission {
359 use Permission::*;
360 match self.0 & 0b111 {
361 0 => None,
362 1 => Content,
363 2 => ExecutableContent,
364 3 => Symlink,
365 4 => Directory,
366 5 => Revision,
367 n => unreachable!("{} mode", n),
368 }
369 }
370}
371
372#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
373pub struct LabelNameId(pub u64);
374
375#[deprecated(since = "7.0.0", note = "FilenameId was renamed to LabelNameId")]
376pub type FilenameId = LabelNameId;
377
378#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
379#[repr(u8)]
380pub enum Permission {
381 None = 0,
382 Content = 1,
383 ExecutableContent = 2,
384 Symlink = 3,
385 Directory = 4,
386 Revision = 5,
387}
388
389impl Permission {
390 #[inline(always)]
396 pub fn to_git(self) -> u16 {
397 use Permission::*;
398 match self {
399 None => 0,
400 Content => 0o100644,
401 ExecutableContent => 0o100755,
402 Symlink => 0o120000,
403 Directory => 0o040000,
404 Revision => 0o160000,
405 }
406 }
407
408 pub fn from_git(mode: u16) -> Option<Permission> {
412 use Permission::*;
413 match mode {
414 0 => Some(None),
415 0o100644 => Some(Content),
416 0o100755 => Some(ExecutableContent),
417 0o120000 => Some(Symlink),
418 0o040000 => Some(Directory),
419 0o160000 => Some(Revision),
420 _ => Option::None,
421 }
422 }
423
424 pub unsafe fn from_git_unchecked(mode: u16) -> Permission {
432 use Permission::*;
433 match mode {
434 0 => None,
435 0o100644 => Content,
436 0o100755 => ExecutableContent,
437 0o120000 => Symlink,
438 0o040000 => Directory,
439 0o160000 => Revision,
440 _ => unreachable!("{} mode", mode),
441 }
442 }
443}