use thiserror::Error;
use crate::NodeType;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct UntypedEdgeLabel(pub(crate) u64);
impl From<u64> for UntypedEdgeLabel {
#[inline(always)]
fn from(n: u64) -> UntypedEdgeLabel {
UntypedEdgeLabel(n)
}
}
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash)]
pub enum EdgeTypingError {
#[error("{src} -> {dst} arcs cannot have labels")]
NodeTypes { src: NodeType, dst: NodeType },
}
impl UntypedEdgeLabel {
pub fn for_edge_type(
&self,
src: NodeType,
dst: NodeType,
transpose_graph: bool,
) -> Result<EdgeLabel, EdgeTypingError> {
use crate::NodeType::*;
let (src, dst) = if transpose_graph {
(dst, src)
} else {
(src, dst)
};
match (src, dst) {
(Snapshot, _) => Ok(EdgeLabel::Branch(self.0.into())),
(Directory, _) => Ok(EdgeLabel::DirEntry(self.0.into())),
(Origin, Snapshot) => Ok(EdgeLabel::Visit(self.0.into())),
_ => Err(EdgeTypingError::NodeTypes { src, dst }),
}
}
}
impl From<EdgeLabel> for UntypedEdgeLabel {
#[inline(always)]
fn from(label: EdgeLabel) -> Self {
UntypedEdgeLabel(match label {
EdgeLabel::Branch(branch) => branch.0,
EdgeLabel::DirEntry(dir_entry) => dir_entry.0,
EdgeLabel::Visit(visit) => visit.0,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EdgeLabel {
Branch(Branch),
DirEntry(DirEntry),
Visit(Visit),
}
macro_rules! impl_edgelabel_convert {
( $variant:ident ( $inner:ty ) ) => {
impl From<$inner> for EdgeLabel {
fn from(v: $inner) -> EdgeLabel {
EdgeLabel::$variant(v)
}
}
impl TryFrom<EdgeLabel> for $inner {
type Error = ();
fn try_from(label: EdgeLabel) -> Result<$inner, Self::Error> {
match label {
EdgeLabel::$variant(v) => Ok(v),
_ => Err(()),
}
}
}
impl From<UntypedEdgeLabel> for $inner {
fn from(label: UntypedEdgeLabel) -> $inner {
<$inner>::from(label.0)
}
}
};
}
impl_edgelabel_convert!(Branch(Branch));
impl_edgelabel_convert!(DirEntry(DirEntry));
impl_edgelabel_convert!(Visit(Visit));
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum VisitType {
Archive,
Misc,
Package,
Push,
Vcs,
VcsCheckout,
Unknown,
}
impl VisitType {
#[rustfmt::skip]
pub fn from_swh_type(swh_type: &str) -> Option<Self> {
Some(match swh_type {
"ftp" |
"tar" |
"tarball-directory"
=> VisitType::Archive,
"content"
=> VisitType::Misc,
"bioconductor" |
"cpan" |
"cran" |
"crates" |
"deb" |
"golang" |
"hackage" |
"hex" |
"opam" |
"nixguix" |
"npm" |
"maven" |
"pubdev" |
"puppet" |
"pypi" |
"rubygems"
=> VisitType::Package,
"deposit"
=> VisitType::Push,
"bzr" |
"cvs" |
"git" |
"hg" |
"svn"
=> VisitType::Vcs,
"hg-checkout" |
"git-checkout" |
"svn-export"
=> VisitType::VcsCheckout,
_ => return None,
})
}
pub fn to_bits(self) -> u8 {
use VisitType::*;
match self {
Unknown => 0b000,
Archive => 0b001,
Misc => 0b010,
Package => 0b011,
Push => 0b100,
Vcs => 0b101,
VcsCheckout => 0b110,
}
}
pub fn from_bits(bits: u8) -> Self {
use VisitType::*;
match bits {
0b000 => Unknown, 0b001 => Archive,
0b010 => Misc,
0b011 => Package,
0b100 => Push,
0b101 => Vcs,
0b110 => VcsCheckout,
0b111 => Unknown, 0b1000..=u8::MAX => {
panic!("VisitType::from_bits got value greater than 0b111");
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum VisitStatus {
Full,
Partial,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Visit(pub(crate) u64);
impl From<u64> for Visit {
#[inline(always)]
fn from(n: u64) -> Visit {
Visit(n)
}
}
impl Visit {
pub fn new(status: VisitStatus, timestamp: u64, visit_type: VisitType) -> Option<Visit> {
let is_full = match status {
VisitStatus::Full => 1u64,
VisitStatus::Partial => 0,
};
let reserved_bits = 0b1000u64;
let visit_type = u64::from(visit_type.to_bits());
assert!(visit_type <= 0b111);
timestamp.checked_shl(5).map(|shifted_timestamp| {
Visit(shifted_timestamp | (is_full << 4) | reserved_bits | visit_type)
})
}
#[inline(always)]
pub fn timestamp(&self) -> u64 {
self.0 >> 5
}
#[inline(always)]
pub fn status(&self) -> VisitStatus {
if self.0 & 0b10000 != 0 {
VisitStatus::Full
} else {
VisitStatus::Partial
}
}
pub fn visit_type(&self) -> VisitType {
VisitType::from_bits((self.0 & 0b111) as u8)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Branch(pub(crate) u64);
impl From<u64> for Branch {
#[inline(always)]
fn from(n: u64) -> Branch {
Branch(n)
}
}
impl Branch {
pub fn new(label_name_id: LabelNameId) -> Option<Branch> {
label_name_id.0.checked_shl(3).map(Branch)
}
#[deprecated(since = "7.0.0", note = "filename_id was renamed label_name_id")]
#[inline(always)]
pub fn filename_id(self) -> LabelNameId {
self.label_name_id()
}
#[inline(always)]
pub fn label_name_id(self) -> LabelNameId {
LabelNameId(self.0 >> 3)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DirEntry(pub(crate) u64);
impl From<u64> for DirEntry {
#[inline(always)]
fn from(n: u64) -> DirEntry {
DirEntry(n)
}
}
impl DirEntry {
pub fn new(permission: Permission, label_name_id: LabelNameId) -> Option<DirEntry> {
label_name_id
.0
.checked_shl(3)
.map(|shifted_label_name_id| DirEntry(shifted_label_name_id | (permission as u64)))
}
#[deprecated(since = "7.0.0", note = "filename_id was renamed label_name_id")]
#[inline(always)]
pub fn filename_id(self) -> LabelNameId {
self.label_name_id()
}
#[inline(always)]
pub fn label_name_id(self) -> LabelNameId {
LabelNameId(self.0 >> 3)
}
pub fn permission(self) -> Option<Permission> {
use Permission::*;
match self.0 & 0b111 {
0 => Some(None),
1 => Some(Content),
2 => Some(ExecutableContent),
3 => Some(Symlink),
4 => Some(Directory),
5 => Some(Revision),
_ => Option::None,
}
}
pub unsafe fn permission_unchecked(self) -> Permission {
use Permission::*;
match self.0 & 0b111 {
0 => None,
1 => Content,
2 => ExecutableContent,
3 => Symlink,
4 => Directory,
5 => Revision,
n => unreachable!("{} mode", n),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LabelNameId(pub u64);
#[deprecated(since = "7.0.0", note = "FilenameId was renamed to LabelNameId")]
pub type FilenameId = LabelNameId;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Permission {
None = 0,
Content = 1,
ExecutableContent = 2,
Symlink = 3,
Directory = 4,
Revision = 5,
}
impl Permission {
#[inline(always)]
pub fn to_git(self) -> u16 {
use Permission::*;
match self {
None => 0,
Content => 0o100644,
ExecutableContent => 0o100755,
Symlink => 0o120000,
Directory => 0o040000,
Revision => 0o160000,
}
}
pub fn from_git(mode: u16) -> Option<Permission> {
use Permission::*;
match mode {
0 => Some(None),
0o100644 => Some(Content),
0o100755 => Some(ExecutableContent),
0o120000 => Some(Symlink),
0o040000 => Some(Directory),
0o160000 => Some(Revision),
_ => Option::None,
}
}
pub unsafe fn from_git_unchecked(mode: u16) -> Permission {
use Permission::*;
match mode {
0 => None,
0o100644 => Content,
0o100755 => ExecutableContent,
0o120000 => Symlink,
0o040000 => Directory,
0o160000 => Revision,
_ => unreachable!("{} mode", mode),
}
}
}