rust/hg-core/src/revlog/changelog.rs
author Raphaël Gomès <rgomes@octobus.net>
Thu, 18 Jul 2024 13:35:39 +0200
changeset 51707 ec7171748350
parent 51700 7f0cb9ee0534
child 51725 bbe59cc5d2e1
permissions -rw-r--r--
rust: apply clippy lints They are at most harmless and at best make the codebase more readable and simpler.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     1
use std::ascii::escape_default;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     2
use std::borrow::Cow;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     3
use std::collections::BTreeMap;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     4
use std::fmt::{Debug, Formatter};
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     5
use std::{iter, str};
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     6
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     7
use chrono::{DateTime, FixedOffset, NaiveDateTime};
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     8
use itertools::{Either, Itertools};
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
     9
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46433
diff changeset
    10
use crate::errors::HgError;
50747
9865af7191d2 rust-changelog: removed now useless early conditional for NULL_REVISION
Georges Racinet <georges.racinet@octobus.net>
parents: 50746
diff changeset
    11
use crate::revlog::Revision;
46744
b1f2c2b336ec rhg: `cat` command: print error messages for missing files
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
    12
use crate::revlog::{Node, NodePrefix};
49937
750409505286 rust-clippy: merge "revlog" module definition and struct implementation
Raphaël Gomès <rgomes@octobus.net>
parents: 49930
diff changeset
    13
use crate::revlog::{Revlog, RevlogEntry, RevlogError};
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
    14
use crate::utils::hg_path::HgPath;
49090
a5ef50becea8 rust-revlog: make `Changelog` and `ManifestLog` unaware of `Repo`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49089
diff changeset
    15
use crate::vfs::Vfs;
51191
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
    16
use crate::{Graph, GraphError, RevlogOpenOptions, UncheckedRevision};
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    17
50409
b47a9316050a rust-changelog: made doc-comments more consistent
Georges Racinet <georges.racinet@octobus.net>
parents: 49937
diff changeset
    18
/// A specialized `Revlog` to work with changelog data format.
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    19
pub struct Changelog {
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    20
    /// The generic `revlog` format.
46433
4b381dbbf8b7 rhg: centralize parsing of `--rev` CLI arguments
Simon Sapin <simon.sapin@octobus.net>
parents: 46431
diff changeset
    21
    pub(crate) revlog: Revlog,
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    22
}
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    23
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    24
impl Changelog {
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    25
    /// Open the `changelog` of a repository given by its root.
51191
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
    26
    pub fn open(
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
    27
        store_vfs: &Vfs,
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
    28
        options: RevlogOpenOptions,
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
    29
    ) -> Result<Self, HgError> {
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
    30
        let revlog = Revlog::open(store_vfs, "00changelog.i", None, options)?;
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    31
        Ok(Self { revlog })
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    32
    }
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    33
50409
b47a9316050a rust-changelog: made doc-comments more consistent
Georges Racinet <georges.racinet@octobus.net>
parents: 49937
diff changeset
    34
    /// Return the `ChangelogRevisionData` for the given node ID.
47969
87e3f878e65f rust: Rename get_node methods to data_for_node, get_rev to data_for_rev
Simon Sapin <simon.sapin@octobus.net>
parents: 47968
diff changeset
    35
    pub fn data_for_node(
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    36
        &self,
46431
645ee7225fab rust: Make NodePrefix allocation-free and Copy, remove NodePrefixRef
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
    37
        node: NodePrefix,
48540
20d0d896183e rhg: Rename some revlog-related types and methods
Simon Sapin <simon.sapin@octobus.net>
parents: 48198
diff changeset
    38
    ) -> Result<ChangelogRevisionData, RevlogError> {
47968
6f579618ea7b rust: Rename the `Revlog::get_node_rev` method to `rev_from_node`
Simon Sapin <simon.sapin@octobus.net>
parents: 47967
diff changeset
    39
        let rev = self.revlog.rev_from_node(node)?;
50977
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    40
        self.entry_for_checked_rev(rev)?.data()
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    41
    }
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    42
50411
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    43
    /// Return the [`ChangelogEntry`] for the given revision number.
49065
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    44
    pub fn entry_for_rev(
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    45
        &self,
50977
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    46
        rev: UncheckedRevision,
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    47
    ) -> Result<ChangelogEntry, RevlogError> {
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    48
        let revlog_entry = self.revlog.get_entry(rev)?;
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    49
        Ok(ChangelogEntry { revlog_entry })
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    50
    }
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    51
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    52
    /// Same as [`Self::entry_for_rev`] for checked revisions.
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    53
    fn entry_for_checked_rev(
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    54
        &self,
49065
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    55
        rev: Revision,
50411
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    56
    ) -> Result<ChangelogEntry, RevlogError> {
50977
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    57
        let revlog_entry = self.revlog.get_entry_for_checked_rev(rev)?;
50411
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    58
        Ok(ChangelogEntry { revlog_entry })
49065
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    59
    }
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    60
50409
b47a9316050a rust-changelog: made doc-comments more consistent
Georges Racinet <georges.racinet@octobus.net>
parents: 49937
diff changeset
    61
    /// Return the [`ChangelogRevisionData`] for the given revision number.
50411
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    62
    ///
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    63
    /// This is a useful shortcut in case the caller does not need the
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    64
    /// generic revlog information (parents, hashes etc). Otherwise
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    65
    /// consider taking a [`ChangelogEntry`] with
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    66
    /// [entry_for_rev](`Self::entry_for_rev`) and doing everything from there.
47969
87e3f878e65f rust: Rename get_node methods to data_for_node, get_rev to data_for_rev
Simon Sapin <simon.sapin@octobus.net>
parents: 47968
diff changeset
    67
    pub fn data_for_rev(
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    68
        &self,
50977
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    69
        rev: UncheckedRevision,
48540
20d0d896183e rhg: Rename some revlog-related types and methods
Simon Sapin <simon.sapin@octobus.net>
parents: 48198
diff changeset
    70
    ) -> Result<ChangelogRevisionData, RevlogError> {
50411
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    71
        self.entry_for_rev(rev)?.data()
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    72
    }
46744
b1f2c2b336ec rhg: `cat` command: print error messages for missing files
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
    73
50977
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
    74
    pub fn node_from_rev(&self, rev: UncheckedRevision) -> Option<&Node> {
47967
6c653d9d41b8 rust: Make private the `index` field of the `Revlog` struct
Simon Sapin <simon.sapin@octobus.net>
parents: 47964
diff changeset
    75
        self.revlog.node_from_rev(rev)
46744
b1f2c2b336ec rhg: `cat` command: print error messages for missing files
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
    76
    }
49065
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    77
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    78
    pub fn rev_from_node(
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    79
        &self,
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    80
        node: NodePrefix,
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    81
    ) -> Result<Revision, RevlogError> {
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    82
        self.revlog.rev_from_node(node)
5d205e476057 rust-revlog: add methods for getting parent revs and entries
Martin von Zweigbergk <martinvonz@google.com>
parents: 49064
diff changeset
    83
    }
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    84
}
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    85
50978
27e773aa607d rust: implement the `Graph` trait for all revlogs
Raphaël Gomès <rgomes@octobus.net>
parents: 50977
diff changeset
    86
impl Graph for Changelog {
27e773aa607d rust: implement the `Graph` trait for all revlogs
Raphaël Gomès <rgomes@octobus.net>
parents: 50977
diff changeset
    87
    fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
27e773aa607d rust: implement the `Graph` trait for all revlogs
Raphaël Gomès <rgomes@octobus.net>
parents: 50977
diff changeset
    88
        self.revlog.parents(rev)
27e773aa607d rust: implement the `Graph` trait for all revlogs
Raphaël Gomès <rgomes@octobus.net>
parents: 50977
diff changeset
    89
    }
27e773aa607d rust: implement the `Graph` trait for all revlogs
Raphaël Gomès <rgomes@octobus.net>
parents: 50977
diff changeset
    90
}
27e773aa607d rust: implement the `Graph` trait for all revlogs
Raphaël Gomès <rgomes@octobus.net>
parents: 50977
diff changeset
    91
50411
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    92
/// A specialized `RevlogEntry` for `changelog` data format
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    93
///
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    94
/// This is a `RevlogEntry` with the added semantics that the associated
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    95
/// data should meet the requirements for `changelog`, materialized by
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    96
/// the fact that `data()` constructs a `ChangelogRevisionData`.
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    97
/// In case that promise would be broken, the `data` method returns an error.
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    98
#[derive(Clone)]
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
    99
pub struct ChangelogEntry<'changelog> {
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   100
    /// Same data, as a generic `RevlogEntry`.
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   101
    pub(crate) revlog_entry: RevlogEntry<'changelog>,
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   102
}
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   103
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   104
impl<'changelog> ChangelogEntry<'changelog> {
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   105
    pub fn data<'a>(
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   106
        &'a self,
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   107
    ) -> Result<ChangelogRevisionData<'changelog>, RevlogError> {
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   108
        let bytes = self.revlog_entry.data()?;
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   109
        if bytes.is_empty() {
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   110
            Ok(ChangelogRevisionData::null())
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   111
        } else {
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   112
            Ok(ChangelogRevisionData::new(bytes).map_err(|err| {
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   113
                RevlogError::Other(HgError::CorruptedRepository(format!(
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   114
                    "Invalid changelog data for revision {}: {:?}",
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   115
                    self.revlog_entry.revision(),
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   116
                    err
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   117
                )))
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   118
            })?)
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   119
        }
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   120
    }
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   121
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   122
    /// Obtain a reference to the underlying `RevlogEntry`.
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   123
    ///
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   124
    /// This allows the caller to access the information that is common
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   125
    /// to all revlog entries: revision number, node id, parent revisions etc.
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   126
    pub fn as_revlog_entry(&self) -> &RevlogEntry {
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   127
        &self.revlog_entry
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   128
    }
50414
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   129
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   130
    pub fn p1_entry(&self) -> Result<Option<ChangelogEntry>, RevlogError> {
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   131
        Ok(self
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   132
            .revlog_entry
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   133
            .p1_entry()?
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   134
            .map(|revlog_entry| Self { revlog_entry }))
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   135
    }
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   136
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   137
    pub fn p2_entry(&self) -> Result<Option<ChangelogEntry>, RevlogError> {
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   138
        Ok(self
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   139
            .revlog_entry
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   140
            .p2_entry()?
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   141
            .map(|revlog_entry| Self { revlog_entry }))
071a6c1d291e rust-changelog: introduce ChangelogEntry parent entries accessors
Georges Racinet <georges.racinet@octobus.net>
parents: 50411
diff changeset
   142
    }
50411
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   143
}
841b13e6e84c rust-changelog: introducing an intermediate `ChangelogEntry`
Georges Racinet <georges.racinet@octobus.net>
parents: 50410
diff changeset
   144
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   145
/// `Changelog` entry which knows how to interpret the `changelog` data bytes.
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   146
#[derive(PartialEq)]
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   147
pub struct ChangelogRevisionData<'changelog> {
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   148
    /// The data bytes of the `changelog` entry.
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   149
    bytes: Cow<'changelog, [u8]>,
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   150
    /// The end offset for the hex manifest (not including the newline)
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   151
    manifest_end: usize,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   152
    /// The end offset for the user+email (not including the newline)
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   153
    user_end: usize,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   154
    /// The end offset for the timestamp+timezone+extras (not including the
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   155
    /// newline)
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   156
    timestamp_end: usize,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   157
    /// The end offset for the file list (not including the newline)
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   158
    files_end: usize,
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   159
}
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   160
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   161
impl<'changelog> ChangelogRevisionData<'changelog> {
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   162
    fn new(bytes: Cow<'changelog, [u8]>) -> Result<Self, HgError> {
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   163
        let mut line_iter = bytes.split(|b| b == &b'\n');
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   164
        let manifest_end = line_iter
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   165
            .next()
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   166
            .expect("Empty iterator from split()?")
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   167
            .len();
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   168
        let user_slice = line_iter.next().ok_or_else(|| {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   169
            HgError::corrupted("Changeset data truncated after manifest line")
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   170
        })?;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   171
        let user_end = manifest_end + 1 + user_slice.len();
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   172
        let timestamp_slice = line_iter.next().ok_or_else(|| {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   173
            HgError::corrupted("Changeset data truncated after user line")
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   174
        })?;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   175
        let timestamp_end = user_end + 1 + timestamp_slice.len();
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   176
        let mut files_end = timestamp_end + 1;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   177
        loop {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   178
            let line = line_iter.next().ok_or_else(|| {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   179
                HgError::corrupted("Changeset data truncated in files list")
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   180
            })?;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   181
            if line.is_empty() {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   182
                if files_end == bytes.len() {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   183
                    // The list of files ended with a single newline (there
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   184
                    // should be two)
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   185
                    return Err(HgError::corrupted(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   186
                        "Changeset data truncated after files list",
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   187
                    ));
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   188
                }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   189
                files_end -= 1;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   190
                break;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   191
            }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   192
            files_end += line.len() + 1;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   193
        }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   194
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   195
        Ok(Self {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   196
            bytes,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   197
            manifest_end,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   198
            user_end,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   199
            timestamp_end,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   200
            files_end,
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   201
        })
49063
cc132255261b rust-changelog: remove special parsing of empty changelog data for null rev
Martin von Zweigbergk <martinvonz@google.com>
parents: 49062
diff changeset
   202
    }
cc132255261b rust-changelog: remove special parsing of empty changelog data for null rev
Martin von Zweigbergk <martinvonz@google.com>
parents: 49062
diff changeset
   203
cc132255261b rust-changelog: remove special parsing of empty changelog data for null rev
Martin von Zweigbergk <martinvonz@google.com>
parents: 49062
diff changeset
   204
    fn null() -> Self {
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   205
        Self::new(Cow::Borrowed(
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   206
            b"0000000000000000000000000000000000000000\n\n0 0\n\n",
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   207
        ))
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   208
        .unwrap()
49063
cc132255261b rust-changelog: remove special parsing of empty changelog data for null rev
Martin von Zweigbergk <martinvonz@google.com>
parents: 49062
diff changeset
   209
    }
cc132255261b rust-changelog: remove special parsing of empty changelog data for null rev
Martin von Zweigbergk <martinvonz@google.com>
parents: 49062
diff changeset
   210
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   211
    /// Return an iterator over the lines of the entry.
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   212
    pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
49062
fb82b5cb8301 rust-changelog: don't skip empty lines when iterating over changeset lines
Martin von Zweigbergk <martinvonz@google.com>
parents: 48541
diff changeset
   213
        self.bytes.split(|b| b == &b'\n')
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   214
    }
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   215
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   216
    /// Return the node id of the `manifest` referenced by this `changelog`
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   217
    /// entry.
47964
796206e74b10 rhg: Reuse manifest when checking status of multiple ambiguous files
Simon Sapin <simon.sapin@octobus.net>
parents: 47963
diff changeset
   218
    pub fn manifest_node(&self) -> Result<Node, HgError> {
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   219
        let manifest_node_hex = &self.bytes[..self.manifest_end];
49063
cc132255261b rust-changelog: remove special parsing of empty changelog data for null rev
Martin von Zweigbergk <martinvonz@google.com>
parents: 49062
diff changeset
   220
        Node::from_hex_for_repo(manifest_node_hex)
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   221
    }
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   222
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   223
    /// The full user string (usually a name followed by an email enclosed in
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   224
    /// angle brackets)
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   225
    pub fn user(&self) -> &[u8] {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   226
        &self.bytes[self.manifest_end + 1..self.user_end]
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   227
    }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   228
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   229
    /// The full timestamp line (timestamp in seconds, offset in seconds, and
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   230
    /// possibly extras)
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   231
    // TODO: We should expose this in a more useful way
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   232
    pub fn timestamp_line(&self) -> &[u8] {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   233
        &self.bytes[self.user_end + 1..self.timestamp_end]
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   234
    }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   235
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   236
    /// Parsed timestamp.
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   237
    pub fn timestamp(&self) -> Result<DateTime<FixedOffset>, HgError> {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   238
        parse_timestamp(self.timestamp_line())
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   239
    }
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   240
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   241
    /// Optional commit extras.
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   242
    pub fn extra(&self) -> Result<BTreeMap<String, Vec<u8>>, HgError> {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   243
        parse_timestamp_line_extra(self.timestamp_line())
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   244
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   245
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   246
    /// The files changed in this revision.
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   247
    pub fn files(&self) -> impl Iterator<Item = &HgPath> {
51363
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   248
        if self.timestamp_end == self.files_end {
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   249
            Either::Left(iter::empty())
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   250
        } else {
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   251
            Either::Right(
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   252
                self.bytes[self.timestamp_end + 1..self.files_end]
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   253
                    .split(|b| b == &b'\n')
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   254
                    .map(HgPath::new),
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   255
            )
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   256
        }
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   257
    }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   258
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   259
    /// The change description.
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   260
    pub fn description(&self) -> &[u8] {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   261
        &self.bytes[self.files_end + 2..]
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   262
    }
45532
c2317b7624fd hg-core: add `Changlog` a specialized `Revlog`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
   263
}
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   264
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   265
impl Debug for ChangelogRevisionData<'_> {
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   266
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   267
        f.debug_struct("ChangelogRevisionData")
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   268
            .field("bytes", &debug_bytes(&self.bytes))
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   269
            .field("manifest", &debug_bytes(&self.bytes[..self.manifest_end]))
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   270
            .field(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   271
                "user",
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   272
                &debug_bytes(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   273
                    &self.bytes[self.manifest_end + 1..self.user_end],
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   274
                ),
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   275
            )
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   276
            .field(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   277
                "timestamp",
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   278
                &debug_bytes(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   279
                    &self.bytes[self.user_end + 1..self.timestamp_end],
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   280
                ),
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   281
            )
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   282
            .field(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   283
                "files",
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   284
                &debug_bytes(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   285
                    &self.bytes[self.timestamp_end + 1..self.files_end],
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   286
                ),
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   287
            )
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   288
            .field(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   289
                "description",
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   290
                &debug_bytes(&self.bytes[self.files_end + 2..]),
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   291
            )
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   292
            .finish()
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   293
    }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   294
}
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   295
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   296
fn debug_bytes(bytes: &[u8]) -> String {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   297
    String::from_utf8_lossy(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   298
        &bytes.iter().flat_map(|b| escape_default(*b)).collect_vec(),
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   299
    )
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   300
    .to_string()
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   301
}
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   302
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   303
/// Parse the raw bytes of the timestamp line from a changelog entry.
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   304
///
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   305
/// According to the documentation in `hg help dates` and the
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   306
/// implementation in `changelog.py`, the format of the timestamp line
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   307
/// is `time tz extra\n` where:
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   308
///
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   309
/// - `time` is an ASCII-encoded signed int or float denoting a UTC timestamp
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   310
///   as seconds since the UNIX epoch.
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   311
///
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   312
/// - `tz` is the timezone offset as an ASCII-encoded signed integer denoting
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   313
///   seconds WEST of UTC (so negative for timezones east of UTC, which is the
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   314
///   opposite of the sign in ISO 8601 timestamps).
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   315
///
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   316
/// - `extra` is an optional set of NUL-delimited key-value pairs, with the key
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   317
///   and value in each pair separated by an ASCII colon. Keys are limited to
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   318
///   ASCII letters, digits, hyphens, and underscores, whereas values can be
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   319
///   arbitrary bytes.
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   320
fn parse_timestamp(
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   321
    timestamp_line: &[u8],
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   322
) -> Result<DateTime<FixedOffset>, HgError> {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   323
    let mut parts = timestamp_line.splitn(3, |c| *c == b' ');
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   324
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   325
    let timestamp_bytes = parts
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   326
        .next()
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   327
        .ok_or_else(|| HgError::corrupted("missing timestamp"))?;
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   328
    let timestamp_str = str::from_utf8(timestamp_bytes).map_err(|e| {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   329
        HgError::corrupted(format!("timestamp is not valid UTF-8: {e}"))
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   330
    })?;
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   331
    let timestamp_utc = timestamp_str
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   332
        .parse()
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   333
        .map_err(|e| {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   334
            HgError::corrupted(format!("failed to parse timestamp: {e}"))
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   335
        })
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   336
        .and_then(|secs| {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   337
            NaiveDateTime::from_timestamp_opt(secs, 0).ok_or_else(|| {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   338
                HgError::corrupted(format!(
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   339
                    "integer timestamp out of valid range: {secs}"
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   340
                ))
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   341
            })
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   342
        })
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   343
        // Attempt to parse the timestamp as a float if we can't parse
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   344
        // it as an int. It doesn't seem like float timestamps are actually
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   345
        // used in practice, but the Python code supports them.
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   346
        .or_else(|_| parse_float_timestamp(timestamp_str))?;
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   347
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   348
    let timezone_bytes = parts
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   349
        .next()
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   350
        .ok_or_else(|| HgError::corrupted("missing timezone"))?;
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   351
    let timezone_secs: i32 = str::from_utf8(timezone_bytes)
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   352
        .map_err(|e| {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   353
            HgError::corrupted(format!("timezone is not valid UTF-8: {e}"))
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   354
        })?
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   355
        .parse()
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   356
        .map_err(|e| {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   357
            HgError::corrupted(format!("timezone is not an integer: {e}"))
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   358
        })?;
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   359
    let timezone = FixedOffset::west_opt(timezone_secs)
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   360
        .ok_or_else(|| HgError::corrupted("timezone offset out of bounds"))?;
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   361
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   362
    Ok(DateTime::from_naive_utc_and_offset(timestamp_utc, timezone))
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   363
}
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   364
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   365
/// Attempt to parse the given string as floating-point timestamp, and
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   366
/// convert the result into a `chrono::NaiveDateTime`.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   367
fn parse_float_timestamp(
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   368
    timestamp_str: &str,
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   369
) -> Result<NaiveDateTime, HgError> {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   370
    let timestamp = timestamp_str.parse::<f64>().map_err(|e| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   371
        HgError::corrupted(format!("failed to parse timestamp: {e}"))
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   372
    })?;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   373
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   374
    // To construct a `NaiveDateTime` we'll need to convert the float
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   375
    // into signed integer seconds and unsigned integer nanoseconds.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   376
    let mut secs = timestamp.trunc() as i64;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   377
    let mut subsecs = timestamp.fract();
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   378
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   379
    // If the timestamp is negative, we need to express the fractional
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   380
    // component as positive nanoseconds since the previous second.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   381
    if timestamp < 0.0 {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   382
        secs -= 1;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   383
        subsecs += 1.0;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   384
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   385
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   386
    // This cast should be safe because the fractional component is
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   387
    // by definition less than 1.0, so this value should not exceed
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   388
    // 1 billion, which is representable as an f64 without loss of
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   389
    // precision and should fit into a u32 without overflowing.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   390
    //
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   391
    // (Any loss of precision in the fractional component will have
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   392
    // already happened at the time of initial parsing; in general,
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   393
    // f64s are insufficiently precise to provide nanosecond-level
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   394
    // precision with present-day timestamps.)
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   395
    let nsecs = (subsecs * 1_000_000_000.0) as u32;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   396
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   397
    NaiveDateTime::from_timestamp_opt(secs, nsecs).ok_or_else(|| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   398
        HgError::corrupted(format!(
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   399
            "float timestamp out of valid range: {timestamp}"
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   400
        ))
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   401
    })
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   402
}
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   403
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   404
/// Decode changeset extra fields.
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   405
///
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   406
/// Extras are null-delimited key-value pairs where the key consists of ASCII
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   407
/// alphanumeric characters plus hyphens and underscores, and the value can
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   408
/// contain arbitrary bytes.
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   409
fn decode_extra(extra: &[u8]) -> Result<BTreeMap<String, Vec<u8>>, HgError> {
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   410
    extra
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   411
        .split(|c| *c == b'\0')
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   412
        .map(|pair| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   413
            let pair = unescape_extra(pair);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   414
            let mut iter = pair.splitn(2, |c| *c == b':');
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   415
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   416
            let key_bytes =
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   417
                iter.next().filter(|k| !k.is_empty()).ok_or_else(|| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   418
                    HgError::corrupted("empty key in changeset extras")
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   419
                })?;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   420
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   421
            let key = str::from_utf8(key_bytes)
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   422
                .ok()
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   423
                .filter(|k| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   424
                    k.chars().all(|c| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   425
                        c.is_ascii_alphanumeric() || c == '_' || c == '-'
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   426
                    })
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   427
                })
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   428
                .ok_or_else(|| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   429
                    let key = String::from_utf8_lossy(key_bytes);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   430
                    HgError::corrupted(format!(
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   431
                        "invalid key in changeset extras: {key}",
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   432
                    ))
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   433
                })?
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   434
                .to_string();
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   435
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   436
            let value = iter.next().map(Into::into).ok_or_else(|| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   437
                HgError::corrupted(format!(
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   438
                    "missing value for changeset extra: {key}"
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   439
                ))
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   440
            })?;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   441
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   442
            Ok((key, value))
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   443
        })
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   444
        .collect()
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   445
}
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   446
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   447
/// Parse the extra fields from a changeset's timestamp line.
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   448
fn parse_timestamp_line_extra(
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   449
    timestamp_line: &[u8],
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   450
) -> Result<BTreeMap<String, Vec<u8>>, HgError> {
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   451
    Ok(timestamp_line
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   452
        .splitn(3, |c| *c == b' ')
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   453
        .nth(2)
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   454
        .map(decode_extra)
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   455
        .transpose()?
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   456
        .unwrap_or_default())
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   457
}
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   458
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   459
/// Decode Mercurial's escaping for changelog extras.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   460
///
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   461
/// The `_string_escape` function in `changelog.py` only escapes 4 characters
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   462
/// (null, backslash, newline, and carriage return) so we only decode those.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   463
///
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   464
/// The Python code also includes a workaround for decoding escaped nuls
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   465
/// that are followed by an ASCII octal digit, since Python's built-in
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   466
/// `string_escape` codec will interpret that as an escaped octal byte value.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   467
/// That workaround is omitted here since we don't support decoding octal.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   468
fn unescape_extra(bytes: &[u8]) -> Vec<u8> {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   469
    let mut output = Vec::with_capacity(bytes.len());
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   470
    let mut input = bytes.iter().copied();
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   471
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   472
    while let Some(c) = input.next() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   473
        if c != b'\\' {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   474
            output.push(c);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   475
            continue;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   476
        }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   477
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   478
        match input.next() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   479
            Some(b'0') => output.push(b'\0'),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   480
            Some(b'\\') => output.push(b'\\'),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   481
            Some(b'n') => output.push(b'\n'),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   482
            Some(b'r') => output.push(b'\r'),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   483
            // The following cases should never occur in theory because any
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   484
            // backslashes in the original input should have been escaped
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   485
            // with another backslash, so it should not be possible to
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   486
            // observe an escape sequence other than the 4 above.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   487
            Some(c) => output.extend_from_slice(&[b'\\', c]),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   488
            None => output.push(b'\\'),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   489
        }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   490
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   491
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   492
    output
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   493
}
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   494
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   495
#[cfg(test)]
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   496
mod tests {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   497
    use super::*;
50410
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   498
    use crate::vfs::Vfs;
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   499
    use crate::NULL_REVISION;
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   500
    use pretty_assertions::assert_eq;
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   501
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   502
    #[test]
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   503
    fn test_create_changelogrevisiondata_invalid() {
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   504
        // Completely empty
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   505
        assert!(ChangelogRevisionData::new(Cow::Borrowed(b"abcd")).is_err());
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   506
        // No newline after manifest
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   507
        assert!(ChangelogRevisionData::new(Cow::Borrowed(b"abcd")).is_err());
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   508
        // No newline after user
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   509
        assert!(ChangelogRevisionData::new(Cow::Borrowed(b"abcd\n")).is_err());
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   510
        // No newline after timestamp
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   511
        assert!(
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   512
            ChangelogRevisionData::new(Cow::Borrowed(b"abcd\n\n0 0")).is_err()
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   513
        );
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   514
        // Missing newline after files
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   515
        assert!(ChangelogRevisionData::new(Cow::Borrowed(
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   516
            b"abcd\n\n0 0\nfile1\nfile2"
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   517
        ))
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   518
        .is_err(),);
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   519
        // Only one newline after files
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   520
        assert!(ChangelogRevisionData::new(Cow::Borrowed(
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   521
            b"abcd\n\n0 0\nfile1\nfile2\n"
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   522
        ))
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   523
        .is_err(),);
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   524
    }
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   525
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   526
    #[test]
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   527
    fn test_create_changelogrevisiondata() {
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   528
        let data = ChangelogRevisionData::new(Cow::Borrowed(
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   529
            b"0123456789abcdef0123456789abcdef01234567
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   530
Some One <someone@example.com>
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   531
0 0
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   532
file1
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   533
file2
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   534
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   535
some
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   536
commit
49096
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   537
message",
07ec9f4f24bf changelog: avoid copying changeset data into `ChangesetRevisionData`
Martin von Zweigbergk <martinvonz@google.com>
parents: 49090
diff changeset
   538
        ))
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   539
        .unwrap();
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   540
        assert_eq!(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   541
            data.manifest_node().unwrap(),
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   542
            Node::from_hex("0123456789abcdef0123456789abcdef01234567")
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   543
                .unwrap()
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   544
        );
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   545
        assert_eq!(data.user(), b"Some One <someone@example.com>");
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   546
        assert_eq!(data.timestamp_line(), b"0 0");
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   547
        assert_eq!(
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   548
            data.files().collect_vec(),
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   549
            vec![HgPath::new("file1"), HgPath::new("file2")]
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   550
        );
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   551
        assert_eq!(data.description(), b"some\ncommit\nmessage");
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   552
    }
50410
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   553
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   554
    #[test]
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   555
    fn test_data_from_rev_null() -> Result<(), RevlogError> {
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   556
        // an empty revlog will be enough for this case
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   557
        let temp = tempfile::tempdir().unwrap();
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   558
        let vfs = Vfs { base: temp.path() };
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   559
        std::fs::write(temp.path().join("foo.i"), b"").unwrap();
51191
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
   560
        let revlog =
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
   561
            Revlog::open(&vfs, "foo.i", None, RevlogOpenOptions::new())
13f58ce70299 rust-revlog: teach the revlog opening code to read the repo options
Raphaël Gomès <rgomes@octobus.net>
parents: 50978
diff changeset
   562
                .unwrap();
50410
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   563
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   564
        let changelog = Changelog { revlog };
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   565
        assert_eq!(
50977
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
   566
            changelog.data_for_rev(NULL_REVISION.into())?,
50410
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   567
            ChangelogRevisionData::null()
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   568
        );
50746
124c44b5cfad rust-revlog: fix RevlogEntry.data() for NULL_REVISION
Georges Racinet <georges.racinet@octobus.net>
parents: 50414
diff changeset
   569
        // same with the intermediate entry object
124c44b5cfad rust-revlog: fix RevlogEntry.data() for NULL_REVISION
Georges Racinet <georges.racinet@octobus.net>
parents: 50414
diff changeset
   570
        assert_eq!(
50977
1928b770e3e7 rust: use the new `UncheckedRevision` everywhere applicable
Raphaël Gomès <rgomes@octobus.net>
parents: 50747
diff changeset
   571
            changelog.entry_for_rev(NULL_REVISION.into())?.data()?,
50746
124c44b5cfad rust-revlog: fix RevlogEntry.data() for NULL_REVISION
Georges Racinet <georges.racinet@octobus.net>
parents: 50414
diff changeset
   572
            ChangelogRevisionData::null()
124c44b5cfad rust-revlog: fix RevlogEntry.data() for NULL_REVISION
Georges Racinet <georges.racinet@octobus.net>
parents: 50414
diff changeset
   573
        );
50410
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   574
        Ok(())
b5dd6d6d6fa6 rust-changelog: added a test for `NULL_REVISION` special case
Georges Racinet <georges.racinet@octobus.net>
parents: 50409
diff changeset
   575
    }
51363
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   576
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   577
    #[test]
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   578
    fn test_empty_files_list() {
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   579
        assert!(ChangelogRevisionData::null()
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   580
            .files()
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   581
            .collect_vec()
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   582
            .is_empty());
d626e5e7bbbe rust-changelog: don't panic on empty file lists
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 50978
diff changeset
   583
    }
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   584
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   585
    #[test]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   586
    fn test_unescape_basic() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   587
        // '\0', '\\', '\n', and '\r' are correctly unescaped.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   588
        let expected = b"AAA\0BBB\\CCC\nDDD\rEEE";
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   589
        let escaped = br"AAA\0BBB\\CCC\nDDD\rEEE";
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   590
        let unescaped = unescape_extra(escaped);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   591
        assert_eq!(&expected[..], &unescaped[..]);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   592
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   593
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   594
    #[test]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   595
    fn test_unescape_unsupported_sequence() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   596
        // Other escape sequences are left unaltered.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   597
        for c in 0u8..255 {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   598
            match c {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   599
                b'0' | b'\\' | b'n' | b'r' => continue,
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   600
                c => {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   601
                    let expected = &[b'\\', c][..];
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   602
                    let unescaped = unescape_extra(expected);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   603
                    assert_eq!(expected, &unescaped[..]);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   604
                }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   605
            }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   606
        }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   607
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   608
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   609
    #[test]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   610
    fn test_unescape_trailing_backslash() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   611
        // Trailing backslashes are OK.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   612
        let expected = br"hi\";
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   613
        let unescaped = unescape_extra(expected);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   614
        assert_eq!(&expected[..], &unescaped[..]);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   615
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   616
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   617
    #[test]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   618
    fn test_unescape_nul_followed_by_octal() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   619
        // Escaped NUL chars followed by octal digits are decoded correctly.
51707
ec7171748350 rust: apply clippy lints
Raphaël Gomès <rgomes@octobus.net>
parents: 51700
diff changeset
   620
        let expected = b"\x0012";
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   621
        let escaped = br"\012";
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   622
        let unescaped = unescape_extra(escaped);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   623
        assert_eq!(&expected[..], &unescaped[..]);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   624
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   625
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   626
    #[test]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   627
    fn test_parse_float_timestamp() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   628
        let test_cases = [
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   629
            // Zero should map to the UNIX epoch.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   630
            ("0.0", "1970-01-01 00:00:00"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   631
            // Negative zero should be the same as positive zero.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   632
            ("-0.0", "1970-01-01 00:00:00"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   633
            // Values without fractional components should work like integers.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   634
            // (Assuming the timestamp is within the limits of f64 precision.)
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   635
            ("1115154970.0", "2005-05-03 21:16:10"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   636
            // We expect some loss of precision in the fractional component
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   637
            // when parsing arbitrary floating-point values.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   638
            ("1115154970.123456789", "2005-05-03 21:16:10.123456716"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   639
            // But representable f64 values should parse losslessly.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   640
            ("1115154970.123456716", "2005-05-03 21:16:10.123456716"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   641
            // Negative fractional components are subtracted from the epoch.
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   642
            ("-1.333", "1969-12-31 23:59:58.667"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   643
        ];
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   644
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   645
        for (input, expected) in test_cases {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   646
            let res = parse_float_timestamp(input).unwrap().to_string();
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   647
            assert_eq!(res, expected);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   648
        }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   649
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   650
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   651
    fn escape_extra(bytes: &[u8]) -> Vec<u8> {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   652
        let mut output = Vec::with_capacity(bytes.len());
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   653
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   654
        for c in bytes.iter().copied() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   655
            output.extend_from_slice(match c {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   656
                b'\0' => &b"\\0"[..],
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   657
                b'\\' => &b"\\\\"[..],
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   658
                b'\n' => &b"\\n"[..],
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   659
                b'\r' => &b"\\r"[..],
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   660
                _ => {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   661
                    output.push(c);
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   662
                    continue;
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   663
                }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   664
            });
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   665
        }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   666
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   667
        output
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   668
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   669
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   670
    fn encode_extra<K, V>(pairs: impl IntoIterator<Item = (K, V)>) -> Vec<u8>
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   671
    where
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   672
        K: AsRef<[u8]>,
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   673
        V: AsRef<[u8]>,
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   674
    {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   675
        let extras = pairs.into_iter().map(|(k, v)| {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   676
            escape_extra(&[k.as_ref(), b":", v.as_ref()].concat())
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   677
        });
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   678
        // Use fully-qualified syntax to avoid a future naming conflict with
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   679
        // the standard library: https://github.com/rust-lang/rust/issues/79524
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   680
        Itertools::intersperse(extras, b"\0".to_vec()).concat()
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   681
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   682
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   683
    #[test]
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   684
    fn test_decode_extra() {
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   685
        let extra = [
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   686
            ("branch".into(), b"default".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   687
            ("key-with-hyphens".into(), b"value1".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   688
            ("key_with_underscores".into(), b"value2".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   689
            ("empty-value".into(), b"".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   690
            ("binary-value".into(), (0u8..=255).collect::<Vec<_>>()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   691
        ]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   692
        .into_iter()
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   693
        .collect::<BTreeMap<String, Vec<u8>>>();
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   694
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   695
        let encoded = encode_extra(&extra);
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   696
        let decoded = decode_extra(&encoded).unwrap();
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   697
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   698
        assert_eq!(extra, decoded);
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   699
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   700
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   701
    #[test]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   702
    fn test_corrupt_extra() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   703
        let test_cases = [
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   704
            (&b""[..], "empty input"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   705
            (&b"\0"[..], "unexpected null byte"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   706
            (&b":empty-key"[..], "empty key"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   707
            (&b"\0leading-null:"[..], "leading null"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   708
            (&b"trailing-null:\0"[..], "trailing null"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   709
            (&b"missing-value"[..], "missing value"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   710
            (&b"$!@# non-alphanum-key:"[..], "non-alphanumeric key"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   711
            (&b"\xF0\x9F\xA6\x80 non-ascii-key:"[..], "non-ASCII key"),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   712
        ];
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   713
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   714
        for (extra, msg) in test_cases {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   715
            assert!(
51707
ec7171748350 rust: apply clippy lints
Raphaël Gomès <rgomes@octobus.net>
parents: 51700
diff changeset
   716
                decode_extra(extra).is_err(),
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   717
                "corrupt extra should have failed to parse: {}",
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   718
                msg
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   719
            );
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   720
        }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   721
    }
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   722
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   723
    #[test]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   724
    fn test_parse_timestamp_line() {
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   725
        let extra = [
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   726
            ("branch".into(), b"default".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   727
            ("key-with-hyphens".into(), b"value1".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   728
            ("key_with_underscores".into(), b"value2".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   729
            ("empty-value".into(), b"".to_vec()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   730
            ("binary-value".into(), (0u8..=255).collect::<Vec<_>>()),
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   731
        ]
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   732
        .into_iter()
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   733
        .collect::<BTreeMap<String, Vec<u8>>>();
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   734
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   735
        let mut line: Vec<u8> = b"1115154970 28800 ".to_vec();
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   736
        line.extend_from_slice(&encode_extra(&extra));
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   737
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   738
        let timestamp = parse_timestamp(&line).unwrap();
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   739
        assert_eq!(&timestamp.to_rfc3339(), "2005-05-03T13:16:10-08:00");
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   740
51393
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   741
        let parsed_extra = parse_timestamp_line_extra(&line).unwrap();
6603a1448f18 hg-core: separate timestamp and extra methods
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51391
diff changeset
   742
        assert_eq!(extra, parsed_extra);
51391
a96ed440450e hg-core: implement timestamp line parsing
Arun Kulshreshtha <akulshreshtha@janestreet.com>
parents: 51369
diff changeset
   743
    }
49064
95da3e99cbd8 rust-changelog: start parsing changeset data
Martin von Zweigbergk <martinvonz@google.com>
parents: 49063
diff changeset
   744
}