5 use crate::revlog::NodePrefix; |
5 use crate::revlog::NodePrefix; |
6 use crate::revlog::Revision; |
6 use crate::revlog::Revision; |
7 use crate::utils::files::get_path_from_bytes; |
7 use crate::utils::files::get_path_from_bytes; |
8 use crate::utils::hg_path::HgPath; |
8 use crate::utils::hg_path::HgPath; |
9 use crate::utils::SliceExt; |
9 use crate::utils::SliceExt; |
10 use std::borrow::Cow; |
|
11 use std::path::PathBuf; |
10 use std::path::PathBuf; |
12 |
11 |
13 /// A specialized `Revlog` to work with file data logs. |
12 /// A specialized `Revlog` to work with file data logs. |
14 pub struct Filelog { |
13 pub struct Filelog { |
15 /// The generic `revlog` format. |
14 /// The generic `revlog` format. |
38 /// changeset. |
37 /// changeset. |
39 pub fn data_for_rev( |
38 pub fn data_for_rev( |
40 &self, |
39 &self, |
41 file_rev: Revision, |
40 file_rev: Revision, |
42 ) -> Result<FilelogEntry, RevlogError> { |
41 ) -> Result<FilelogEntry, RevlogError> { |
43 let data = self.revlog.get_rev_data(file_rev)?; |
42 let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?; |
44 Ok(FilelogEntry(data.into())) |
43 Ok(FilelogEntry(data.into())) |
45 } |
44 } |
46 } |
45 } |
47 |
46 |
48 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { |
47 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { |
49 let encoded_bytes = |
48 let encoded_bytes = |
50 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); |
49 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); |
51 get_path_from_bytes(&encoded_bytes).into() |
50 get_path_from_bytes(&encoded_bytes).into() |
52 } |
51 } |
53 |
52 |
54 pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>); |
53 pub struct FilelogEntry(Vec<u8>); |
55 |
54 |
56 impl<'filelog> FilelogEntry<'filelog> { |
55 impl FilelogEntry { |
57 /// Split into metadata and data |
56 /// Split into metadata and data |
58 pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { |
57 /// Returns None if there is no metadata, so the entire entry is data. |
|
58 fn split_metadata(&self) -> Result<Option<(&[u8], &[u8])>, HgError> { |
59 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; |
59 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; |
60 |
60 |
61 if let Some(rest) = self.0.drop_prefix(DELIMITER) { |
61 if let Some(rest) = self.0.drop_prefix(DELIMITER) { |
62 if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { |
62 if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { |
63 Ok((Some(metadata), data)) |
63 Ok(Some((metadata, data))) |
64 } else { |
64 } else { |
65 Err(HgError::corrupted( |
65 Err(HgError::corrupted( |
66 "Missing metadata end delimiter in filelog entry", |
66 "Missing metadata end delimiter in filelog entry", |
67 )) |
67 )) |
68 } |
68 } |
|
69 } else { |
|
70 Ok(None) |
|
71 } |
|
72 } |
|
73 |
|
74 /// Split into metadata and data |
|
75 pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { |
|
76 if let Some((metadata, data)) = self.split_metadata()? { |
|
77 Ok((Some(metadata), data)) |
69 } else { |
78 } else { |
70 Ok((None, &self.0)) |
79 Ok((None, &self.0)) |
71 } |
80 } |
72 } |
81 } |
73 |
82 |
74 /// Returns the file contents at this revision, stripped of any metadata |
83 /// Returns the file contents at this revision, stripped of any metadata |
75 pub fn data(&self) -> Result<&[u8], HgError> { |
84 pub fn data(&self) -> Result<&[u8], HgError> { |
76 let (_metadata, data) = self.split()?; |
85 let (_metadata, data) = self.split()?; |
77 Ok(data) |
86 Ok(data) |
78 } |
87 } |
|
88 |
|
89 /// Consume the entry, and convert it into data, discarding any metadata, |
|
90 /// if present. |
|
91 pub fn into_data(self) -> Result<Vec<u8>, HgError> { |
|
92 if let Some((_metadata, data)) = self.split_metadata()? { |
|
93 Ok(data.to_owned()) |
|
94 } else { |
|
95 Ok(self.0) |
|
96 } |
|
97 } |
79 } |
98 } |