##// END OF EJS Templates
rust: Add a Filelog struct that wraps Revlog...
Simon Sapin -
r48775:4d2a5ca0 default
parent child Browse files
Show More
@@ -0,0 +1,79 b''
1 use crate::errors::HgError;
2 use crate::repo::Repo;
3 use crate::revlog::path_encode::path_encode;
4 use crate::revlog::revlog::{Revlog, RevlogError};
5 use crate::revlog::NodePrefix;
6 use crate::revlog::Revision;
7 use crate::utils::files::get_path_from_bytes;
8 use crate::utils::hg_path::HgPath;
9 use crate::utils::SliceExt;
10 use std::borrow::Cow;
11 use std::path::PathBuf;
12
13 /// A specialized `Revlog` to work with file data logs.
14 pub struct Filelog {
15 /// The generic `revlog` format.
16 revlog: Revlog,
17 }
18
19 impl Filelog {
20 pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, RevlogError> {
21 let index_path = store_path(file_path, b".i");
22 let data_path = store_path(file_path, b".d");
23 let revlog = Revlog::open(repo, index_path, Some(&data_path))?;
24 Ok(Self { revlog })
25 }
26
27 /// The given node ID is that of the file as found in a manifest, not of a
28 /// changeset.
29 pub fn get_node(
30 &self,
31 file_node: impl Into<NodePrefix>,
32 ) -> Result<FilelogEntry, RevlogError> {
33 let file_rev = self.revlog.get_node_rev(file_node.into())?;
34 self.get_rev(file_rev)
35 }
36
37 /// The given revision is that of the file as found in a manifest, not of a
38 /// changeset.
39 pub fn get_rev(
40 &self,
41 file_rev: Revision,
42 ) -> Result<FilelogEntry, RevlogError> {
43 let data = self.revlog.get_rev_data(file_rev)?;
44 Ok(FilelogEntry(data.into()))
45 }
46 }
47
48 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
49 let encoded_bytes =
50 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
51 get_path_from_bytes(&encoded_bytes).into()
52 }
53
54 pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>);
55
56 impl<'filelog> FilelogEntry<'filelog> {
57 /// Split into metadata and data
58 pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> {
59 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n'];
60
61 if let Some(rest) = self.0.drop_prefix(DELIMITER) {
62 if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) {
63 Ok((Some(metadata), data))
64 } else {
65 Err(HgError::corrupted(
66 "Missing metadata end delimiter in filelog entry",
67 ))
68 }
69 } else {
70 Ok((None, &self.0))
71 }
72 }
73
74 /// Returns the file contents at this revision, stripped of any metadata
75 pub fn data(&self) -> Result<&[u8], HgError> {
76 let (_metadata, data) = self.split()?;
77 Ok(data)
78 }
79 }
@@ -5,15 +5,11 b''
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use std::path::PathBuf;
9
10 use crate::repo::Repo;
8 use crate::repo::Repo;
11 use crate::revlog::path_encode::path_encode;
12 use crate::revlog::revlog::Revlog;
13 use crate::revlog::revlog::RevlogError;
9 use crate::revlog::revlog::RevlogError;
14 use crate::revlog::Node;
10 use crate::revlog::Node;
15 use crate::utils::files::get_path_from_bytes;
11
16 use crate::utils::hg_path::{HgPath, HgPathBuf};
12 use crate::utils::hg_path::HgPathBuf;
17
13
18 pub struct CatOutput {
14 pub struct CatOutput {
19 /// Whether any file in the manifest matched the paths given as CLI
15 /// Whether any file in the manifest matched the paths given as CLI
@@ -27,8 +23,6 b' pub struct CatOutput {'
27 pub node: Node,
23 pub node: Node,
28 }
24 }
29
25
30 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
31
32 /// Output the given revision of files
26 /// Output the given revision of files
33 ///
27 ///
34 /// * `root`: Repository root
28 /// * `root`: Repository root
@@ -54,26 +48,10 b" pub fn cat<'a>("
54 if cat_file.as_bytes() == manifest_file.as_bytes() {
48 if cat_file.as_bytes() == manifest_file.as_bytes() {
55 *is_matched = true;
49 *is_matched = true;
56 found_any = true;
50 found_any = true;
57 let index_path = store_path(manifest_file, b".i");
51 let file_log = repo.filelog(manifest_file)?;
58 let data_path = store_path(manifest_file, b".d");
59
60 let file_log =
61 Revlog::open(repo, &index_path, Some(&data_path))?;
62 let file_node = Node::from_hex_for_repo(node_bytes)?;
52 let file_node = Node::from_hex_for_repo(node_bytes)?;
63 let file_rev = file_log.get_node_rev(file_node.into())?;
53 let entry = file_log.get_node(file_node)?;
64 let data = file_log.get_rev_data(file_rev)?;
54 bytes.extend(entry.data()?)
65 if data.starts_with(&METADATA_DELIMITER) {
66 let end_delimiter_position = data
67 [METADATA_DELIMITER.len()..]
68 .windows(METADATA_DELIMITER.len())
69 .position(|bytes| bytes == METADATA_DELIMITER);
70 if let Some(position) = end_delimiter_position {
71 let offset = METADATA_DELIMITER.len() * 2;
72 bytes.extend(data[position + offset..].iter());
73 }
74 } else {
75 bytes.extend(data);
76 }
77 }
55 }
78 }
56 }
79 }
57 }
@@ -91,9 +69,3 b" pub fn cat<'a>("
91 node,
69 node,
92 })
70 })
93 }
71 }
94
95 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
96 let encoded_bytes =
97 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
98 get_path_from_bytes(&encoded_bytes).into()
99 }
@@ -7,8 +7,10 b' use crate::errors::HgError;'
7 use crate::errors::HgResultExt;
7 use crate::errors::HgResultExt;
8 use crate::manifest::{Manifest, Manifestlog};
8 use crate::manifest::{Manifest, Manifestlog};
9 use crate::requirements;
9 use crate::requirements;
10 use crate::revlog::filelog::Filelog;
10 use crate::revlog::revlog::RevlogError;
11 use crate::revlog::revlog::RevlogError;
11 use crate::utils::files::get_path_from_bytes;
12 use crate::utils::files::get_path_from_bytes;
13 use crate::utils::hg_path::HgPath;
12 use crate::utils::SliceExt;
14 use crate::utils::SliceExt;
13 use crate::vfs::{is_dir, is_file, Vfs};
15 use crate::vfs::{is_dir, is_file, Vfs};
14 use crate::{exit_codes, Node};
16 use crate::{exit_codes, Node};
@@ -346,6 +348,10 b' impl Repo {'
346 Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
348 Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
347 manifest.get_node(manifest_node.into())
349 manifest.get_node(manifest_node.into())
348 }
350 }
351
352 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, RevlogError> {
353 Filelog::open(self, path)
354 }
349 }
355 }
350
356
351 /// Lazily-initialized component of `Repo` with interior mutability
357 /// Lazily-initialized component of `Repo` with interior mutability
@@ -11,6 +11,7 b' mod nodemap_docket;'
11 pub mod path_encode;
11 pub mod path_encode;
12 pub use node::{FromHexError, Node, NodePrefix};
12 pub use node::{FromHexError, Node, NodePrefix};
13 pub mod changelog;
13 pub mod changelog;
14 pub mod filelog;
14 pub mod index;
15 pub mod index;
15 pub mod manifest;
16 pub mod manifest;
16 pub mod patch;
17 pub mod patch;
@@ -74,6 +74,7 b' pub trait SliceExt {'
74 fn trim(&self) -> &Self;
74 fn trim(&self) -> &Self;
75 fn drop_prefix(&self, needle: &Self) -> Option<&Self>;
75 fn drop_prefix(&self, needle: &Self) -> Option<&Self>;
76 fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>;
76 fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>;
77 fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])>;
77 }
78 }
78
79
79 impl SliceExt for [u8] {
80 impl SliceExt for [u8] {
@@ -134,6 +135,14 b' impl SliceExt for [u8] {'
134 let b = iter.next()?;
135 let b = iter.next()?;
135 Some((a, b))
136 Some((a, b))
136 }
137 }
138
139 fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])> {
140 if let Some(pos) = find_slice_in_slice(self, separator) {
141 Some((&self[..pos], &self[pos + separator.len()..]))
142 } else {
143 None
144 }
145 }
137 }
146 }
138
147
139 pub trait Escaped {
148 pub trait Escaped {
General Comments 0
You need to be logged in to leave comments. Login now