use std::{
fmt::{self, Debug, Display},
ops::{Add, Range, Sub},
rc::Rc,
str::FromStr,
sync::Arc,
};
use super::error::ErrorKind;
use bitflags::bitflags;
use chrono::{
DateTime as ChronoDateTime, FixedOffset, Local, LocalResult, NaiveDateTime, TimeZone,
};
use sha1::{Digest, Sha1};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub(crate) enum RepositoryRequire {
Revlogv1,
Store,
FnCache,
Shared,
RelShared,
DotEncode,
ParentDelta,
GeneralDelta,
Manifestv2,
TreeManifest,
ExpSparse,
}
impl FromStr for RepositoryRequire {
type Err = ErrorKind;
fn from_str(value: &str) -> Result<RepositoryRequire, ErrorKind> {
use RepositoryRequire::*;
match value {
"revlogv1" => Ok(Revlogv1),
"store" => Ok(Store),
"fncache" => Ok(FnCache),
"shared" => Ok(Shared),
"relshared" => Ok(RelShared),
"dotencode" => Ok(DotEncode),
"parentdelta" => Ok(ParentDelta),
"generaldelta" => Ok(GeneralDelta),
"manifestv2" => Ok(Manifestv2),
"treemanifest" => Ok(TreeManifest),
"exp-sparse" => Ok(ExpSparse),
other => Err(ErrorKind::UnknownRequirement(other.into())),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Revision(pub u32);
impl Revision {
pub fn range_to(self, lim: Self) -> RevisionRange {
RevisionRange(self.0, lim.0)
}
pub fn range(self) -> RevisionRange {
RevisionRange(self.0, std::u32::MAX)
}
}
impl From<u32> for Revision {
fn from(value: u32) -> Self {
Self(value)
}
}
impl Add<u32> for Revision {
type Output = Self;
fn add(self, other: u32) -> Self {
Self(self.0 + other)
}
}
impl Sub<u32> for Revision {
type Output = Self;
fn sub(self, other: u32) -> Self {
assert!(self.0 >= other);
Self(self.0 - other)
}
}
impl From<Revision> for usize {
fn from(value: Revision) -> usize {
value.0 as usize
}
}
impl<'a> IntoIterator for &'a Revision {
type Item = Revision;
type IntoIter = RevisionRange;
fn into_iter(self) -> Self::IntoIter {
self.range()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct RevisionRange(u32, u32);
impl Iterator for RevisionRange {
type Item = Revision;
fn next(&mut self) -> Option<Self::Item> {
if self.0 < self.1 {
let ret = Revision(self.0);
self.0 += 1;
Some(ret)
} else {
None
}
}
}
impl DoubleEndedIterator for RevisionRange {
fn next_back(&mut self) -> Option<Revision> {
if self.0 < self.1 {
self.1 -= 1;
let ret = Revision(self.1);
Some(ret)
} else {
None
}
}
}
impl From<Range<usize>> for RevisionRange {
fn from(value: Range<usize>) -> Self {
Self(value.start as u32, value.end as u32)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u16)]
pub enum Version {
Revlog0 = 0,
RevlogNG = 1,
}
bitflags! {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Features: u16 {
const INLINE = 1;
const GENERAL_DELTA = 1 << 1;
}
}
bitflags! {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct IdxFlags: u16 {
const EXTSTORED = 1 << 13;
const CENSORED = 1 << 15;
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct RevisionLogHeader {
pub version: Version,
pub features: Features,
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NodeHash([u8; 20]);
const HEX_CHARS: &[u8] = b"0123456789abcdef";
impl NodeHash {
pub fn to_hex(self) -> String {
let mut v = Vec::with_capacity(40);
for &byte in &self.0 {
v.push(HEX_CHARS[(byte >> 4) as usize]);
v.push(HEX_CHARS[(byte & 0xf) as usize]);
}
unsafe { String::from_utf8_unchecked(v) }
}
pub fn from_slice(data: &[u8]) -> Self {
(&Sha1::digest(data)[..]).into()
}
}
impl AsRef<[u8]> for NodeHash {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl Display for NodeHash {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.to_hex(), fmt)
}
}
impl Debug for NodeHash {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "NodeHash({})", self)
}
}
impl<'a> From<&'a [u8]> for NodeHash {
fn from(value: &'a [u8]) -> Self {
let mut data: [u8; 20] = Default::default();
data.copy_from_slice(value);
Self(data)
}
}
impl FromStr for NodeHash {
type Err = ErrorKind;
fn from_str(s: &str) -> Result<Self, ErrorKind> {
let mut ret = Self([0; 20]);
for idx in 0..ret.0.len() {
ret.0[idx] = match u8::from_str_radix(&s[(idx * 2)..(idx * 2 + 2)], 16) {
Ok(v) => v,
Err(_) => return Err(ErrorKind::Parser),
}
}
Ok(ret)
}
}
#[derive(Debug, Copy, Clone)]
pub struct RevisionLogEntry {
pub offset: u64, pub flags: IdxFlags, pub compressed_len: u32, pub len: Option<u32>, pub baserev: Option<Revision>, pub linkrev: Revision, pub p1: Option<Revision>, pub p2: Option<Revision>, pub nodeid: NodeHash, }
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Delta {
pub(crate) fragments: Vec<Fragment>,
}
impl Delta {
pub fn new(fragments: Vec<Fragment>) -> Result<Self, ErrorKind> {
Ok(Delta { fragments })
}
pub fn fragments(&self) -> &[Fragment] {
self.fragments.as_slice()
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Fragment {
pub start: usize,
pub end: usize,
pub content: Rc<[u8]>,
}
impl Fragment {
pub fn content_length(&self) -> usize {
self.content.len()
}
}
impl Debug for Fragment {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"Fragment(\nstart:{}\nend:{}\ncontent:{:?}\n)",
self.start,
self.end,
std::str::from_utf8(&self.content)
)
}
}
#[derive(Debug, Clone)]
pub enum Chunk {
Literal(Arc<[u8]>),
Deltas(Delta),
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DateTime(ChronoDateTime<FixedOffset>);
impl DateTime {
#[inline]
pub fn new(dt: ChronoDateTime<FixedOffset>) -> Self {
DateTime(dt)
}
pub fn now() -> Self {
let now = Local::now();
DateTime(now.with_timezone(now.offset()))
}
pub fn from_timestamp(secs: i64, tz_offset_secs: i32) -> Result<Self, ErrorKind> {
let tz = FixedOffset::west_opt(tz_offset_secs).ok_or_else(|| {
ErrorKind::InvalidDateTime(format!("timezone offset out of range: {}", tz_offset_secs))
})?;
let dt = match tz.timestamp_opt(secs, 0) {
LocalResult::Single(dt) => dt,
_ => {
return Err(ErrorKind::InvalidDateTime(format!(
"seconds out of range: {}",
secs
)));
}
};
Ok(Self::new(dt))
}
pub fn from_rfc3339(rfc3339: &str) -> Result<Self, ErrorKind> {
let dt = ChronoDateTime::parse_from_rfc3339(rfc3339)?;
Ok(Self::new(dt))
}
#[inline]
pub fn timestamp_secs(&self) -> i64 {
self.0.timestamp()
}
#[inline]
pub fn tz_offset_secs(&self) -> i32 {
self.0.offset().utc_minus_local()
}
#[inline]
pub fn as_chrono(&self) -> &ChronoDateTime<FixedOffset> {
&self.0
}
#[inline]
pub fn into_chrono(self) -> ChronoDateTime<FixedOffset> {
self.0
}
}
impl Display for DateTime {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct Timestamp(i64);
impl Timestamp {
pub fn now() -> Self {
DateTime::now().into()
}
pub fn from_timestamp_nanos(ts: i64) -> Self {
Timestamp(ts)
}
pub fn timestamp_nanos(self) -> i64 {
self.0
}
}
impl From<DateTime> for Timestamp {
fn from(dt: DateTime) -> Self {
Timestamp(dt.0.timestamp_nanos())
}
}
impl From<Timestamp> for DateTime {
fn from(ts: Timestamp) -> Self {
let ts_secs = ts.0 / 1_000_000_000;
let ts_nsecs = (ts.0 % 1_000_000_000) as u32;
DateTime::new(ChronoDateTime::<FixedOffset>::from_utc(
NaiveDateTime::from_timestamp(ts_secs, ts_nsecs),
FixedOffset::west(0),
))
}
}
pub struct MercurialTag {
pub node: NodeHash,
pub name: String,
}
impl FromStr for MercurialTag {
type Err = ErrorKind;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let mut parts = value.split_whitespace();
if let (Some(node), Some(name)) = (
parts
.next()
.and_then(|x| if x.len() == 40 { Some(x) } else { None })
.and_then(|x| x.parse().ok()),
parts.next().map(String::from),
) {
Ok(Self { node, name })
} else {
Err(ErrorKind::Parser)
}
}
}