Mercurial > hg
comparison rust/hg-core/src/revlog/changelog.rs @ 50411:841b13e6e84c
rust-changelog: introducing an intermediate `ChangelogEntry`
Before this change, client code needing to extract, e.g, the Node ID and the
description from a changeset had no other choice than calling both
`entry_for_rev()` and `data_for_rev()`. This duplicates some (limited) computation, and
more importantly imposes bad hygiene for client code: at some point of developement,
the client code would have to pass over both entry and data in its internal layers,
which at some point of development would raise the question whether they are consistent.
We introduce the intermediate `ChangelogEntry` from which both conversion to the generic
`RevlogEntry` and extraction of `ChangelogRevisionData` are possible.
It might grow some convenience methods in the future.
We keep the `data_for_rev()` method of `Changelog` for compatibility, pointing users at the more
powerful alternative.
author | Georges Racinet <georges.racinet@octobus.net> |
---|---|
date | Wed, 29 Mar 2023 20:50:42 +0200 |
parents | b5dd6d6d6fa6 |
children | 071a6c1d291e |
comparison
equal
deleted
inserted
replaced
50410:b5dd6d6d6fa6 | 50411:841b13e6e84c |
---|---|
1 use crate::errors::HgError; | 1 use crate::errors::HgError; |
2 use crate::revlog::Revision; | |
3 use crate::revlog::{Node, NodePrefix}; | 2 use crate::revlog::{Node, NodePrefix}; |
3 use crate::revlog::{Revision, NULL_REVISION}; | |
4 use crate::revlog::{Revlog, RevlogEntry, RevlogError}; | 4 use crate::revlog::{Revlog, RevlogEntry, RevlogError}; |
5 use crate::utils::hg_path::HgPath; | 5 use crate::utils::hg_path::HgPath; |
6 use crate::vfs::Vfs; | 6 use crate::vfs::Vfs; |
7 use itertools::Itertools; | 7 use itertools::Itertools; |
8 use std::ascii::escape_default; | 8 use std::ascii::escape_default; |
30 ) -> Result<ChangelogRevisionData, RevlogError> { | 30 ) -> Result<ChangelogRevisionData, RevlogError> { |
31 let rev = self.revlog.rev_from_node(node)?; | 31 let rev = self.revlog.rev_from_node(node)?; |
32 self.data_for_rev(rev) | 32 self.data_for_rev(rev) |
33 } | 33 } |
34 | 34 |
35 /// Return the `RevlogEntry` for the given revision number. | 35 /// Return the [`ChangelogEntry`] for the given revision number. |
36 pub fn entry_for_rev( | 36 pub fn entry_for_rev( |
37 &self, | 37 &self, |
38 rev: Revision, | 38 rev: Revision, |
39 ) -> Result<RevlogEntry, RevlogError> { | 39 ) -> Result<ChangelogEntry, RevlogError> { |
40 self.revlog.get_entry(rev) | 40 let revlog_entry = self.revlog.get_entry(rev)?; |
41 Ok(ChangelogEntry { revlog_entry }) | |
41 } | 42 } |
42 | 43 |
43 /// Return the [`ChangelogRevisionData`] for the given revision number. | 44 /// Return the [`ChangelogRevisionData`] for the given revision number. |
45 /// | |
46 /// This is a useful shortcut in case the caller does not need the | |
47 /// generic revlog information (parents, hashes etc). Otherwise | |
48 /// consider taking a [`ChangelogEntry`] with | |
49 /// [entry_for_rev](`Self::entry_for_rev`) and doing everything from there. | |
44 pub fn data_for_rev( | 50 pub fn data_for_rev( |
45 &self, | 51 &self, |
46 rev: Revision, | 52 rev: Revision, |
47 ) -> Result<ChangelogRevisionData, RevlogError> { | 53 ) -> Result<ChangelogRevisionData, RevlogError> { |
48 let bytes = self.revlog.get_rev_data(rev)?; | 54 if rev == NULL_REVISION { |
55 return Ok(ChangelogRevisionData::null()); | |
56 } | |
57 self.entry_for_rev(rev)?.data() | |
58 } | |
59 | |
60 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> { | |
61 self.revlog.node_from_rev(rev) | |
62 } | |
63 | |
64 pub fn rev_from_node( | |
65 &self, | |
66 node: NodePrefix, | |
67 ) -> Result<Revision, RevlogError> { | |
68 self.revlog.rev_from_node(node) | |
69 } | |
70 } | |
71 | |
72 /// A specialized `RevlogEntry` for `changelog` data format | |
73 /// | |
74 /// This is a `RevlogEntry` with the added semantics that the associated | |
75 /// data should meet the requirements for `changelog`, materialized by | |
76 /// the fact that `data()` constructs a `ChangelogRevisionData`. | |
77 /// In case that promise would be broken, the `data` method returns an error. | |
78 #[derive(Clone)] | |
79 pub struct ChangelogEntry<'changelog> { | |
80 /// Same data, as a generic `RevlogEntry`. | |
81 pub(crate) revlog_entry: RevlogEntry<'changelog>, | |
82 } | |
83 | |
84 impl<'changelog> ChangelogEntry<'changelog> { | |
85 pub fn data<'a>( | |
86 &'a self, | |
87 ) -> Result<ChangelogRevisionData<'changelog>, RevlogError> { | |
88 let bytes = self.revlog_entry.data()?; | |
49 if bytes.is_empty() { | 89 if bytes.is_empty() { |
50 Ok(ChangelogRevisionData::null()) | 90 Ok(ChangelogRevisionData::null()) |
51 } else { | 91 } else { |
52 Ok(ChangelogRevisionData::new(bytes).map_err(|err| { | 92 Ok(ChangelogRevisionData::new(bytes).map_err(|err| { |
53 RevlogError::Other(HgError::CorruptedRepository(format!( | 93 RevlogError::Other(HgError::CorruptedRepository(format!( |
54 "Invalid changelog data for revision {}: {:?}", | 94 "Invalid changelog data for revision {}: {:?}", |
55 rev, err | 95 self.revlog_entry.revision(), |
96 err | |
56 ))) | 97 ))) |
57 })?) | 98 })?) |
58 } | 99 } |
59 } | 100 } |
60 | 101 |
61 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> { | 102 /// Obtain a reference to the underlying `RevlogEntry`. |
62 self.revlog.node_from_rev(rev) | 103 /// |
63 } | 104 /// This allows the caller to access the information that is common |
64 | 105 /// to all revlog entries: revision number, node id, parent revisions etc. |
65 pub fn rev_from_node( | 106 pub fn as_revlog_entry(&self) -> &RevlogEntry { |
66 &self, | 107 &self.revlog_entry |
67 node: NodePrefix, | |
68 ) -> Result<Revision, RevlogError> { | |
69 self.revlog.rev_from_node(node) | |
70 } | 108 } |
71 } | 109 } |
72 | 110 |
73 /// `Changelog` entry which knows how to interpret the `changelog` data bytes. | 111 /// `Changelog` entry which knows how to interpret the `changelog` data bytes. |
74 #[derive(PartialEq)] | 112 #[derive(PartialEq)] |