1use std::{
2 fmt::{self, Display},
3 ops::{Add, Range, Sub},
4 rc::Rc,
5 str::FromStr,
6 sync::Arc,
7};
8
9use super::error::ErrorKind;
10use bitflags::bitflags;
11use chrono::{
12 DateTime as ChronoDateTime, FixedOffset, Local, LocalResult, NaiveDateTime, TimeZone,
13};
14use sha1::{Digest, Sha1};
15
16#[derive(Debug, Clone, Eq, PartialEq, Hash)]
21pub enum RepositoryRequire {
22 Revlogv1,
24 Store,
26 FnCache,
28 Shared,
30 RelShared,
32 DotEncode,
34 ParentDelta,
38 GeneralDelta,
42 Manifestv2,
44 TreeManifest,
47 ExpSparse,
49 ShareSafe,
51 Unknown(String),
53}
54
55impl FromStr for RepositoryRequire {
56 type Err = ErrorKind;
57
58 fn from_str(value: &str) -> Result<RepositoryRequire, ErrorKind> {
59 use RepositoryRequire::*;
60 match value {
61 "revlogv1" => Ok(Revlogv1),
62 "store" => Ok(Store),
63 "fncache" => Ok(FnCache),
64 "shared" => Ok(Shared),
65 "relshared" => Ok(RelShared),
66 "dotencode" => Ok(DotEncode),
67 "parentdelta" => Ok(ParentDelta),
68 "generaldelta" => Ok(GeneralDelta),
69 "manifestv2" => Ok(Manifestv2),
70 "treemanifest" => Ok(TreeManifest),
71 "exp-sparse" => Ok(ExpSparse),
72 "share-safe" => Ok(ShareSafe),
73 other => Err(ErrorKind::UnknownRequirement(Self::Unknown(other.into()))),
74 }
75 }
76}
77
78impl std::fmt::Display for RepositoryRequire {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 use RepositoryRequire::*;
81 match self {
82 Revlogv1 => "revlogv1".fmt(f),
83 Store => "store".fmt(f),
84 FnCache => "fncache".fmt(f),
85 Shared => "shared".fmt(f),
86 RelShared => "relshared".fmt(f),
87 DotEncode => "dotencode".fmt(f),
88 ParentDelta => "parentdelta".fmt(f),
89 GeneralDelta => "generaldelta".fmt(f),
90 Manifestv2 => "manifestv2".fmt(f),
91 TreeManifest => "treemanifest".fmt(f),
92 ExpSparse => "exp-sparse".fmt(f),
93 ShareSafe => "share-safe".fmt(f),
94 Unknown(s) => s.fmt(f),
95 }
96 }
97}
98
99#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
101pub struct Revision(pub u32);
102
103impl Revision {
104 pub fn range_to(self, lim: Self) -> RevisionRange {
106 RevisionRange(self.0, lim.0)
107 }
108
109 pub fn range(self) -> RevisionRange {
111 RevisionRange(self.0, u32::MAX)
112 }
113}
114
115impl From<u32> for Revision {
116 fn from(value: u32) -> Self {
117 Self(value)
118 }
119}
120
121impl Add<u32> for Revision {
122 type Output = Self;
123
124 fn add(self, other: u32) -> Self {
125 Self(self.0 + other)
126 }
127}
128
129impl Sub<u32> for Revision {
130 type Output = Self;
131
132 fn sub(self, other: u32) -> Self {
133 assert!(self.0 >= other);
134 Self(self.0 - other)
135 }
136}
137
138impl From<Revision> for usize {
139 fn from(value: Revision) -> usize {
140 value.0 as usize
141 }
142}
143
144impl<'a> IntoIterator for &'a Revision {
147 type Item = Revision;
148 type IntoIter = RevisionRange;
149
150 fn into_iter(self) -> Self::IntoIter {
151 self.range()
152 }
153}
154
155#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
157pub struct RevisionRange(u32, u32);
158
159impl Iterator for RevisionRange {
160 type Item = Revision;
161
162 fn next(&mut self) -> Option<Self::Item> {
163 if self.0 < self.1 {
164 let ret = Revision(self.0);
165 self.0 += 1;
166 Some(ret)
167 } else {
168 None
169 }
170 }
171}
172
173impl DoubleEndedIterator for RevisionRange {
174 fn next_back(&mut self) -> Option<Revision> {
175 if self.0 < self.1 {
176 self.1 -= 1;
177 let ret = Revision(self.1);
178 Some(ret)
179 } else {
180 None
181 }
182 }
183}
184
185impl From<Range<usize>> for RevisionRange {
186 fn from(value: Range<usize>) -> Self {
187 Self(value.start as u32, value.end as u32)
188 }
189}
190
191#[derive(Clone, Copy, Debug, Eq, PartialEq)]
193#[repr(u16)]
194pub enum Version {
195 Revlog0 = 0,
196 RevlogNG = 1,
197}
198
199bitflags! {
200 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
202 pub struct Features: u16 {
203 const INLINE = 1;
204 const GENERAL_DELTA = 1 << 1;
205 }
206}
207
208bitflags! {
209 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
211 pub struct IdxFlags: u16 {
212 const EXTSTORED = 1 << 13;
213 const CENSORED = 1 << 15;
214 }
215}
216
217#[derive(Copy, Clone, Debug, Eq, PartialEq)]
219pub struct RevisionLogHeader {
220 pub version: Version,
221 pub features: Features,
222}
223
224#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
225pub struct NodeHash([u8; 20]);
226
227const HEX_CHARS: &[u8] = b"0123456789abcdef";
228
229impl NodeHash {
230 pub fn to_hex(self) -> String {
231 let mut v = Vec::with_capacity(40);
232 for &byte in &self.0 {
233 v.push(HEX_CHARS[(byte >> 4) as usize]);
234 v.push(HEX_CHARS[(byte & 0xf) as usize]);
235 }
236
237 unsafe { String::from_utf8_unchecked(v) }
238 }
239
240 pub fn from_slice(data: &[u8]) -> Self {
241 (&Sha1::digest(data)[..]).into()
242 }
243}
244
245impl AsRef<[u8]> for NodeHash {
246 fn as_ref(&self) -> &[u8] {
247 &self.0[..]
248 }
249}
250
251impl std::fmt::Display for NodeHash {
252 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
253 Display::fmt(&self.to_hex(), fmt)
254 }
255}
256
257impl std::fmt::Debug for NodeHash {
258 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
259 write!(fmt, "NodeHash({})", self)
260 }
261}
262
263impl<'a> From<&'a [u8]> for NodeHash {
264 fn from(value: &'a [u8]) -> Self {
265 let mut data: [u8; 20] = Default::default();
266 data.copy_from_slice(value);
267 Self(data)
268 }
269}
270
271impl FromStr for NodeHash {
272 type Err = ErrorKind;
273
274 fn from_str(s: &str) -> Result<Self, ErrorKind> {
275 let mut ret = Self([0; 20]);
276
277 for idx in 0..ret.0.len() {
278 ret.0[idx] = match u8::from_str_radix(&s[(idx * 2)..(idx * 2 + 2)], 16) {
279 Ok(v) => v,
280 Err(_) => return Err(ErrorKind::Parser),
281 }
282 }
283
284 Ok(ret)
285 }
286}
287
288#[derive(Debug, Copy, Clone)]
290pub struct RevisionLogEntry {
291 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, }
301
302#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
303pub struct Delta {
304 pub(crate) fragments: Vec<Fragment>,
306}
307
308impl Delta {
309 pub fn new(fragments: Vec<Fragment>) -> Result<Self, ErrorKind> {
312 Ok(Delta { fragments })
313 }
314 pub fn fragments(&self) -> &[Fragment] {
315 self.fragments.as_slice()
316 }
317}
318#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
320pub struct Fragment {
321 pub start: usize,
322 pub end: usize,
323 pub content: Rc<[u8]>,
324}
325
326impl Fragment {
327 pub fn content_length(&self) -> usize {
329 self.content.len()
330 }
331}
332
333impl std::fmt::Debug for Fragment {
334 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
335 write!(
336 fmt,
337 "Fragment(\nstart:{}\nend:{}\ncontent:{:?}\n)",
338 self.start,
339 self.end,
340 std::str::from_utf8(&self.content)
341 )
342 }
343}
344
345#[derive(Debug, Clone)]
346pub enum Chunk {
347 Literal(Arc<[u8]>),
349 Deltas(Delta),
351}
352
353#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
354pub struct DateTime(ChronoDateTime<FixedOffset>);
355
356impl DateTime {
357 #[inline]
358 pub fn new(dt: ChronoDateTime<FixedOffset>) -> Self {
359 DateTime(dt)
360 }
361
362 pub fn now() -> Self {
363 let now = Local::now();
364 DateTime(now.with_timezone(now.offset()))
365 }
366
367 pub fn from_timestamp(secs: i64, tz_offset_secs: i32) -> Result<Self, ErrorKind> {
368 let tz = FixedOffset::west_opt(tz_offset_secs).ok_or_else(|| {
369 ErrorKind::InvalidDateTime(format!("timezone offset out of range: {}", tz_offset_secs))
370 })?;
371 let dt = match tz.timestamp_opt(secs, 0) {
372 LocalResult::Single(dt) => dt,
373 _ => {
374 return Err(ErrorKind::InvalidDateTime(format!(
375 "seconds out of range: {}",
376 secs
377 )));
378 }
379 };
380 Ok(Self::new(dt))
381 }
382
383 pub fn from_rfc3339(rfc3339: &str) -> Result<Self, ErrorKind> {
388 let dt = ChronoDateTime::parse_from_rfc3339(rfc3339)?;
389 Ok(Self::new(dt))
390 }
391
392 #[inline]
394 pub fn timestamp_secs(&self) -> i64 {
395 self.0.timestamp()
396 }
397
398 #[inline]
401 pub fn tz_offset_secs(&self) -> i32 {
402 self.0.offset().utc_minus_local()
404 }
405
406 #[inline]
407 pub fn as_chrono(&self) -> &ChronoDateTime<FixedOffset> {
408 &self.0
409 }
410
411 #[inline]
412 pub fn into_chrono(self) -> ChronoDateTime<FixedOffset> {
413 self.0
414 }
415}
416
417impl Display for DateTime {
418 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
419 write!(fmt, "{}", self.0)
420 }
421}
422
423#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
425pub struct Timestamp(i64);
426
427impl Timestamp {
428 pub fn now() -> Self {
429 DateTime::now().into()
430 }
431
432 pub fn from_timestamp_nanos(ts: i64) -> Self {
433 Timestamp(ts)
434 }
435
436 pub fn timestamp_nanos(self) -> i64 {
437 self.0
438 }
439}
440
441impl From<DateTime> for Timestamp {
442 fn from(dt: DateTime) -> Self {
443 Timestamp(dt.0.timestamp_nanos())
444 }
445}
446
447impl From<Timestamp> for DateTime {
448 fn from(ts: Timestamp) -> Self {
449 let ts_secs = ts.0 / 1_000_000_000;
450 let ts_nsecs = (ts.0 % 1_000_000_000) as u32;
451 DateTime::new(ChronoDateTime::<FixedOffset>::from_utc(
452 NaiveDateTime::from_timestamp(ts_secs, ts_nsecs),
453 FixedOffset::west(0),
454 ))
455 }
456}
457
458pub struct MercurialTag {
459 pub node: NodeHash,
460 pub name: String,
461}
462
463impl FromStr for MercurialTag {
464 type Err = ErrorKind;
465
466 fn from_str(value: &str) -> Result<Self, Self::Err> {
467 let mut parts = value.split_whitespace();
468 if let (Some(node), Some(name)) = (
469 parts
470 .next()
471 .and_then(|x| if x.len() == 40 { Some(x) } else { None })
472 .and_then(|x| x.parse().ok()),
473 parts.next().map(String::from),
474 ) {
475 Ok(Self { node, name })
476 } else {
477 Err(ErrorKind::Parser)
478 }
479 }
480}