2 use crate::repo::Repo; |
2 use crate::repo::Repo; |
3 use crate::revlog::revlog::{Revlog, RevlogError}; |
3 use crate::revlog::revlog::{Revlog, RevlogError}; |
4 use crate::revlog::Revision; |
4 use crate::revlog::Revision; |
5 use crate::revlog::{Node, NodePrefix}; |
5 use crate::revlog::{Node, NodePrefix}; |
6 use crate::utils::hg_path::HgPath; |
6 use crate::utils::hg_path::HgPath; |
|
7 use crate::utils::SliceExt; |
7 |
8 |
8 /// A specialized `Revlog` to work with `manifest` data format. |
9 /// A specialized `Revlog` to work with `manifest` data format. |
9 pub struct Manifestlog { |
10 pub struct Manifestlog { |
10 /// The generic `revlog` format. |
11 /// The generic `revlog` format. |
11 revlog: Revlog, |
12 revlog: Revlog, |
53 pub struct Manifest { |
54 pub struct Manifest { |
54 bytes: Vec<u8>, |
55 bytes: Vec<u8>, |
55 } |
56 } |
56 |
57 |
57 impl Manifest { |
58 impl Manifest { |
58 /// Return an iterator over the lines of the entry. |
59 pub fn iter( |
59 pub fn lines(&self) -> impl Iterator<Item = &[u8]> { |
60 &self, |
|
61 ) -> impl Iterator<Item = Result<ManifestEntry, HgError>> { |
60 self.bytes |
62 self.bytes |
61 .split(|b| b == &b'\n') |
63 .split(|b| b == &b'\n') |
62 .filter(|line| !line.is_empty()) |
64 .filter(|line| !line.is_empty()) |
63 } |
65 .map(|line| { |
64 |
66 let (path, rest) = line.split_2(b'\0').ok_or_else(|| { |
65 /// Return an iterator over the files of the entry. |
|
66 pub fn files(&self) -> impl Iterator<Item = Result<&HgPath, HgError>> { |
|
67 self.lines().filter(|line| !line.is_empty()).map(|line| { |
|
68 let pos = |
|
69 line.iter().position(|x| x == &b'\0').ok_or_else(|| { |
|
70 HgError::corrupted("manifest line should contain \\0") |
67 HgError::corrupted("manifest line should contain \\0") |
71 })?; |
68 })?; |
72 Ok(HgPath::new(&line[..pos])) |
69 let path = HgPath::new(path); |
73 }) |
70 let (hex_node_id, flags) = match rest.split_last() { |
74 } |
71 Some((&b'x', rest)) => (rest, Some(b'x')), |
75 |
72 Some((&b'l', rest)) => (rest, Some(b'l')), |
76 /// Return an iterator over the files of the entry. |
73 Some((&b't', rest)) => (rest, Some(b't')), |
77 pub fn files_with_nodes( |
74 _ => (rest, None), |
78 &self, |
75 }; |
79 ) -> impl Iterator<Item = Result<(&HgPath, &[u8]), HgError>> { |
76 Ok(ManifestEntry { |
80 self.lines().filter(|line| !line.is_empty()).map(|line| { |
77 path, |
81 let pos = |
78 hex_node_id, |
82 line.iter().position(|x| x == &b'\0').ok_or_else(|| { |
79 flags, |
83 HgError::corrupted("manifest line should contain \\0") |
80 }) |
84 })?; |
81 }) |
85 let hash_start = pos + 1; |
|
86 let hash_end = hash_start + 40; |
|
87 Ok((HgPath::new(&line[..pos]), &line[hash_start..hash_end])) |
|
88 }) |
|
89 } |
82 } |
90 |
83 |
91 /// If the given path is in this manifest, return its filelog node ID |
84 /// If the given path is in this manifest, return its filelog node ID |
92 pub fn find_file(&self, path: &HgPath) -> Result<Option<Node>, HgError> { |
85 pub fn find_file( |
|
86 &self, |
|
87 path: &HgPath, |
|
88 ) -> Result<Option<ManifestEntry>, HgError> { |
93 // TODO: use binary search instead of linear scan. This may involve |
89 // TODO: use binary search instead of linear scan. This may involve |
94 // building (and caching) an index of the byte indicex of each manifest |
90 // building (and caching) an index of the byte indicex of each manifest |
95 // line. |
91 // line. |
96 for entry in self.files_with_nodes() { |
92 |
97 let (manifest_path, node) = entry?; |
93 // TODO: use try_find when available (if still using linear scan) |
98 if manifest_path == path { |
94 // https://github.com/rust-lang/rust/issues/63178 |
99 return Ok(Some(Node::from_hex_for_repo(node)?)); |
95 for entry in self.iter() { |
|
96 let entry = entry?; |
|
97 if entry.path == path { |
|
98 return Ok(Some(entry)); |
100 } |
99 } |
101 } |
100 } |
102 Ok(None) |
101 Ok(None) |
103 } |
102 } |
104 } |
103 } |
|
104 |
|
105 /// `Manifestlog` entry which knows how to interpret the `manifest` data bytes. |
|
106 #[derive(Debug)] |
|
107 pub struct ManifestEntry<'manifest> { |
|
108 pub path: &'manifest HgPath, |
|
109 pub hex_node_id: &'manifest [u8], |
|
110 |
|
111 /// `Some` values are b'x', b'l', or 't' |
|
112 pub flags: Option<u8>, |
|
113 } |
|
114 |
|
115 impl ManifestEntry<'_> { |
|
116 pub fn node_id(&self) -> Result<Node, HgError> { |
|
117 Node::from_hex_for_repo(self.hex_node_id) |
|
118 } |
|
119 } |