rhg: centralize parsing of `--rev` CLI arguments
authorSimon Sapin <simon.sapin@octobus.net>
Tue, 26 Jan 2021 18:31:46 +0100
changeset 46433 4b381dbbf8b7
parent 46432 18a261b11b20
child 46434 3e2d539d0d1a
rhg: centralize parsing of `--rev` CLI arguments This new module will be the place to implement more of the revset language when we do so. Differential Revision: https://phab.mercurial-scm.org/D9873
rust/hg-core/src/lib.rs
rust/hg-core/src/operations/cat.rs
rust/hg-core/src/operations/debugdata.rs
rust/hg-core/src/operations/list_tracked_files.rs
rust/hg-core/src/revlog/changelog.rs
rust/hg-core/src/revlog/revlog.rs
rust/hg-core/src/revset.rs
--- a/rust/hg-core/src/lib.rs	Mon Jan 25 18:25:26 2021 +0100
+++ b/rust/hg-core/src/lib.rs	Tue Jan 26 18:31:46 2021 +0100
@@ -28,6 +28,7 @@
 pub use revlog::*;
 pub mod config;
 pub mod operations;
+pub mod revset;
 pub mod utils;
 
 use crate::utils::hg_path::{HgPathBuf, HgPathError};
--- a/rust/hg-core/src/operations/cat.rs	Mon Jan 25 18:25:26 2021 +0100
+++ b/rust/hg-core/src/operations/cat.rs	Tue Jan 26 18:31:46 2021 +0100
@@ -15,8 +15,6 @@
 use crate::revlog::revlog::Revlog;
 use crate::revlog::revlog::RevlogError;
 use crate::revlog::Node;
-use crate::revlog::NodePrefix;
-use crate::revlog::Revision;
 use crate::utils::files::get_path_from_bytes;
 use crate::utils::hg_path::{HgPath, HgPathBuf};
 
@@ -77,23 +75,15 @@
 /// * `files`: The files to output.
 pub fn cat(
     repo: &Repo,
-    rev: &str,
+    revset: &str,
     files: &[HgPathBuf],
 ) -> Result<Vec<u8>, CatRevError> {
+    let rev = crate::revset::resolve_single(revset, repo)?;
     let changelog = Changelog::open(repo)?;
     let manifest = Manifest::open(repo)?;
-
-    let changelog_entry = match rev.parse::<Revision>() {
-        Ok(rev) => changelog.get_rev(rev)?,
-        _ => {
-            let changelog_node = NodePrefix::from_hex(&rev)
-                .map_err(|_| CatRevErrorKind::InvalidRevision)?;
-            changelog.get_node(changelog_node)?
-        }
-    };
+    let changelog_entry = changelog.get_rev(rev)?;
     let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
         .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
-
     let manifest_entry = manifest.get_node(manifest_node.into())?;
     let mut bytes = vec![];
 
--- a/rust/hg-core/src/operations/debugdata.rs	Mon Jan 25 18:25:26 2021 +0100
+++ b/rust/hg-core/src/operations/debugdata.rs	Tue Jan 26 18:31:46 2021 +0100
@@ -7,8 +7,6 @@
 
 use crate::repo::Repo;
 use crate::revlog::revlog::{Revlog, RevlogError};
-use crate::revlog::NodePrefix;
-use crate::revlog::Revision;
 
 /// Kind of data to debug
 #[derive(Debug, Copy, Clone)]
@@ -79,7 +77,7 @@
 /// Dump the contents data of a revision.
 pub fn debug_data(
     repo: &Repo,
-    rev: &str,
+    revset: &str,
     kind: DebugDataKind,
 ) -> Result<Vec<u8>, DebugDataError> {
     let index_file = match kind {
@@ -87,16 +85,8 @@
         DebugDataKind::Manifest => "00manifest.i",
     };
     let revlog = Revlog::open(repo, index_file, None)?;
-
-    let data = match rev.parse::<Revision>() {
-        Ok(rev) => revlog.get_rev_data(rev)?,
-        _ => {
-            let node = NodePrefix::from_hex(&rev)
-                .map_err(|_| DebugDataErrorKind::InvalidRevision)?;
-            let rev = revlog.get_node_rev(node)?;
-            revlog.get_rev_data(rev)?
-        }
-    };
-
+    let rev =
+        crate::revset::resolve_rev_number_or_hex_prefix(revset, &revlog)?;
+    let data = revlog.get_rev_data(rev)?;
     Ok(data)
 }
--- a/rust/hg-core/src/operations/list_tracked_files.rs	Mon Jan 25 18:25:26 2021 +0100
+++ b/rust/hg-core/src/operations/list_tracked_files.rs	Tue Jan 26 18:31:46 2021 +0100
@@ -9,9 +9,8 @@
 use crate::repo::Repo;
 use crate::revlog::changelog::Changelog;
 use crate::revlog::manifest::{Manifest, ManifestEntry};
-use crate::revlog::node::{Node, NodePrefix};
+use crate::revlog::node::Node;
 use crate::revlog::revlog::RevlogError;
-use crate::revlog::Revision;
 use crate::utils::hg_path::HgPath;
 use crate::{DirstateParseError, EntryState};
 use rayon::prelude::*;
@@ -137,19 +136,12 @@
 /// List files under Mercurial control at a given revision.
 pub fn list_rev_tracked_files(
     repo: &Repo,
-    rev: &str,
+    revset: &str,
 ) -> Result<FilesForRev, ListRevTrackedFilesError> {
+    let rev = crate::revset::resolve_single(revset, repo)?;
     let changelog = Changelog::open(repo)?;
     let manifest = Manifest::open(repo)?;
-
-    let changelog_entry = match rev.parse::<Revision>() {
-        Ok(rev) => changelog.get_rev(rev)?,
-        _ => {
-            let changelog_node = NodePrefix::from_hex(&rev)
-                .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
-            changelog.get_node(changelog_node)?
-        }
-    };
+    let changelog_entry = changelog.get_rev(rev)?;
     let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
         .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
     let manifest_entry = manifest.get_node(manifest_node.into())?;
--- a/rust/hg-core/src/revlog/changelog.rs	Mon Jan 25 18:25:26 2021 +0100
+++ b/rust/hg-core/src/revlog/changelog.rs	Tue Jan 26 18:31:46 2021 +0100
@@ -6,7 +6,7 @@
 /// A specialized `Revlog` to work with `changelog` data format.
 pub struct Changelog {
     /// The generic `revlog` format.
-    revlog: Revlog,
+    pub(crate) revlog: Revlog,
 }
 
 impl Changelog {
--- a/rust/hg-core/src/revlog/revlog.rs	Mon Jan 25 18:25:26 2021 +0100
+++ b/rust/hg-core/src/revlog/revlog.rs	Tue Jan 26 18:31:46 2021 +0100
@@ -150,6 +150,11 @@
         found_by_prefix.ok_or(RevlogError::InvalidRevision)
     }
 
+    /// Returns whether the given revision exists in this revlog.
+    pub fn has_rev(&self, rev: Revision) -> bool {
+        self.index.get_entry(rev).is_some()
+    }
+
     /// Return the full data associated to a revision.
     ///
     /// All entries required to build the final data out of deltas will be
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/revset.rs	Tue Jan 26 18:31:46 2021 +0100
@@ -0,0 +1,53 @@
+//! The revset query language
+//!
+//! <https://www.mercurial-scm.org/repo/hg/help/revsets>
+
+use crate::repo::Repo;
+use crate::revlog::changelog::Changelog;
+use crate::revlog::revlog::{Revlog, RevlogError};
+use crate::revlog::NodePrefix;
+use crate::revlog::{Revision, NULL_REVISION};
+
+/// Resolve a query string into a single revision.
+///
+/// Only some of the revset language is implemented yet.
+pub fn resolve_single(
+    input: &str,
+    repo: &Repo,
+) -> Result<Revision, RevlogError> {
+    let changelog = Changelog::open(repo)?;
+
+    match resolve_rev_number_or_hex_prefix(input, &changelog.revlog) {
+        Err(RevlogError::InvalidRevision) => {} // Try other syntax
+        result => return result,
+    }
+
+    if input == "null" {
+        return Ok(NULL_REVISION);
+    }
+
+    // TODO: support for the rest of the language here.
+
+    Err(RevlogError::InvalidRevision)
+}
+
+/// Resolve the small subset of the language suitable for revlogs other than
+/// the changelog, such as in `hg debugdata --manifest` CLI argument.
+///
+/// * A non-negative decimal integer for a revision number, or
+/// * An hexadecimal string, for the unique node ID that starts with this
+///   prefix
+pub fn resolve_rev_number_or_hex_prefix(
+    input: &str,
+    revlog: &Revlog,
+) -> Result<Revision, RevlogError> {
+    if let Ok(integer) = input.parse::<i32>() {
+        if integer >= 0 && revlog.has_rev(integer) {
+            return Ok(integer);
+        }
+    }
+    if let Ok(prefix) = NodePrefix::from_hex(input) {
+        return revlog.get_node_rev(prefix);
+    }
+    Err(RevlogError::InvalidRevision)
+}