##// END OF EJS Templates
rust: Add a Timestamp struct instead of abusing Duration...
Simon Sapin -
r47871:5d62243c default
parent child Browse files
Show More
@@ -1,505 +1,505
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
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 crate::dirstate::parsers::Timestamp;
8 use crate::errors::HgError;
9 use crate::errors::HgError;
9 use crate::revlog::node::NULL_NODE;
10 use crate::revlog::node::NULL_NODE;
10 use crate::{
11 use crate::{
11 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
12 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
12 pack_dirstate, parse_dirstate,
13 pack_dirstate, parse_dirstate,
13 utils::{
14 utils::{
14 files::normalize_case,
15 files::normalize_case,
15 hg_path::{HgPath, HgPathBuf},
16 hg_path::{HgPath, HgPathBuf},
16 },
17 },
17 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
18 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
18 DirstateParents, FastHashMap, StateMap,
19 DirstateParents, FastHashMap, StateMap,
19 };
20 };
20 use micro_timer::timed;
21 use micro_timer::timed;
21 use std::collections::HashSet;
22 use std::collections::HashSet;
22 use std::convert::TryInto;
23 use std::convert::TryInto;
23 use std::iter::FromIterator;
24 use std::iter::FromIterator;
24 use std::ops::Deref;
25 use std::ops::Deref;
25 use std::time::Duration;
26
26
27 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
27 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
28
28
29 const MTIME_UNSET: i32 = -1;
29 const MTIME_UNSET: i32 = -1;
30
30
31 #[derive(Default)]
31 #[derive(Default)]
32 pub struct DirstateMap {
32 pub struct DirstateMap {
33 state_map: StateMap,
33 state_map: StateMap,
34 pub copy_map: CopyMap,
34 pub copy_map: CopyMap,
35 file_fold_map: Option<FileFoldMap>,
35 file_fold_map: Option<FileFoldMap>,
36 pub dirs: Option<DirsMultiset>,
36 pub dirs: Option<DirsMultiset>,
37 pub all_dirs: Option<DirsMultiset>,
37 pub all_dirs: Option<DirsMultiset>,
38 non_normal_set: Option<HashSet<HgPathBuf>>,
38 non_normal_set: Option<HashSet<HgPathBuf>>,
39 other_parent_set: Option<HashSet<HgPathBuf>>,
39 other_parent_set: Option<HashSet<HgPathBuf>>,
40 parents: Option<DirstateParents>,
40 parents: Option<DirstateParents>,
41 dirty_parents: bool,
41 dirty_parents: bool,
42 }
42 }
43
43
44 /// Should only really be used in python interface code, for clarity
44 /// Should only really be used in python interface code, for clarity
45 impl Deref for DirstateMap {
45 impl Deref for DirstateMap {
46 type Target = StateMap;
46 type Target = StateMap;
47
47
48 fn deref(&self) -> &Self::Target {
48 fn deref(&self) -> &Self::Target {
49 &self.state_map
49 &self.state_map
50 }
50 }
51 }
51 }
52
52
53 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
54 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
55 iter: I,
55 iter: I,
56 ) -> Self {
56 ) -> Self {
57 Self {
57 Self {
58 state_map: iter.into_iter().collect(),
58 state_map: iter.into_iter().collect(),
59 ..Self::default()
59 ..Self::default()
60 }
60 }
61 }
61 }
62 }
62 }
63
63
64 impl DirstateMap {
64 impl DirstateMap {
65 pub fn new() -> Self {
65 pub fn new() -> Self {
66 Self::default()
66 Self::default()
67 }
67 }
68
68
69 pub fn clear(&mut self) {
69 pub fn clear(&mut self) {
70 self.state_map = StateMap::default();
70 self.state_map = StateMap::default();
71 self.copy_map.clear();
71 self.copy_map.clear();
72 self.file_fold_map = None;
72 self.file_fold_map = None;
73 self.non_normal_set = None;
73 self.non_normal_set = None;
74 self.other_parent_set = None;
74 self.other_parent_set = None;
75 self.set_parents(&DirstateParents {
75 self.set_parents(&DirstateParents {
76 p1: NULL_NODE,
76 p1: NULL_NODE,
77 p2: NULL_NODE,
77 p2: NULL_NODE,
78 })
78 })
79 }
79 }
80
80
81 /// Add a tracked file to the dirstate
81 /// Add a tracked file to the dirstate
82 pub fn add_file(
82 pub fn add_file(
83 &mut self,
83 &mut self,
84 filename: &HgPath,
84 filename: &HgPath,
85 old_state: EntryState,
85 old_state: EntryState,
86 entry: DirstateEntry,
86 entry: DirstateEntry,
87 ) -> Result<(), DirstateMapError> {
87 ) -> Result<(), DirstateMapError> {
88 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 if old_state == EntryState::Unknown || old_state == EntryState::Removed
89 {
89 {
90 if let Some(ref mut dirs) = self.dirs {
90 if let Some(ref mut dirs) = self.dirs {
91 dirs.add_path(filename)?;
91 dirs.add_path(filename)?;
92 }
92 }
93 }
93 }
94 if old_state == EntryState::Unknown {
94 if old_state == EntryState::Unknown {
95 if let Some(ref mut all_dirs) = self.all_dirs {
95 if let Some(ref mut all_dirs) = self.all_dirs {
96 all_dirs.add_path(filename)?;
96 all_dirs.add_path(filename)?;
97 }
97 }
98 }
98 }
99 self.state_map.insert(filename.to_owned(), entry.to_owned());
99 self.state_map.insert(filename.to_owned(), entry.to_owned());
100
100
101 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
102 self.get_non_normal_other_parent_entries()
102 self.get_non_normal_other_parent_entries()
103 .0
103 .0
104 .insert(filename.to_owned());
104 .insert(filename.to_owned());
105 }
105 }
106
106
107 if entry.size == SIZE_FROM_OTHER_PARENT {
107 if entry.size == SIZE_FROM_OTHER_PARENT {
108 self.get_non_normal_other_parent_entries()
108 self.get_non_normal_other_parent_entries()
109 .1
109 .1
110 .insert(filename.to_owned());
110 .insert(filename.to_owned());
111 }
111 }
112 Ok(())
112 Ok(())
113 }
113 }
114
114
115 /// Mark a file as removed in the dirstate.
115 /// Mark a file as removed in the dirstate.
116 ///
116 ///
117 /// The `size` parameter is used to store sentinel values that indicate
117 /// The `size` parameter is used to store sentinel values that indicate
118 /// the file's previous state. In the future, we should refactor this
118 /// the file's previous state. In the future, we should refactor this
119 /// to be more explicit about what that state is.
119 /// to be more explicit about what that state is.
120 pub fn remove_file(
120 pub fn remove_file(
121 &mut self,
121 &mut self,
122 filename: &HgPath,
122 filename: &HgPath,
123 old_state: EntryState,
123 old_state: EntryState,
124 size: i32,
124 size: i32,
125 ) -> Result<(), DirstateMapError> {
125 ) -> Result<(), DirstateMapError> {
126 if old_state != EntryState::Unknown && old_state != EntryState::Removed
126 if old_state != EntryState::Unknown && old_state != EntryState::Removed
127 {
127 {
128 if let Some(ref mut dirs) = self.dirs {
128 if let Some(ref mut dirs) = self.dirs {
129 dirs.delete_path(filename)?;
129 dirs.delete_path(filename)?;
130 }
130 }
131 }
131 }
132 if old_state == EntryState::Unknown {
132 if old_state == EntryState::Unknown {
133 if let Some(ref mut all_dirs) = self.all_dirs {
133 if let Some(ref mut all_dirs) = self.all_dirs {
134 all_dirs.add_path(filename)?;
134 all_dirs.add_path(filename)?;
135 }
135 }
136 }
136 }
137
137
138 if let Some(ref mut file_fold_map) = self.file_fold_map {
138 if let Some(ref mut file_fold_map) = self.file_fold_map {
139 file_fold_map.remove(&normalize_case(filename));
139 file_fold_map.remove(&normalize_case(filename));
140 }
140 }
141 self.state_map.insert(
141 self.state_map.insert(
142 filename.to_owned(),
142 filename.to_owned(),
143 DirstateEntry {
143 DirstateEntry {
144 state: EntryState::Removed,
144 state: EntryState::Removed,
145 mode: 0,
145 mode: 0,
146 size,
146 size,
147 mtime: 0,
147 mtime: 0,
148 },
148 },
149 );
149 );
150 self.get_non_normal_other_parent_entries()
150 self.get_non_normal_other_parent_entries()
151 .0
151 .0
152 .insert(filename.to_owned());
152 .insert(filename.to_owned());
153 Ok(())
153 Ok(())
154 }
154 }
155
155
156 /// Remove a file from the dirstate.
156 /// Remove a file from the dirstate.
157 /// Returns `true` if the file was previously recorded.
157 /// Returns `true` if the file was previously recorded.
158 pub fn drop_file(
158 pub fn drop_file(
159 &mut self,
159 &mut self,
160 filename: &HgPath,
160 filename: &HgPath,
161 old_state: EntryState,
161 old_state: EntryState,
162 ) -> Result<bool, DirstateMapError> {
162 ) -> Result<bool, DirstateMapError> {
163 let exists = self.state_map.remove(filename).is_some();
163 let exists = self.state_map.remove(filename).is_some();
164
164
165 if exists {
165 if exists {
166 if old_state != EntryState::Removed {
166 if old_state != EntryState::Removed {
167 if let Some(ref mut dirs) = self.dirs {
167 if let Some(ref mut dirs) = self.dirs {
168 dirs.delete_path(filename)?;
168 dirs.delete_path(filename)?;
169 }
169 }
170 }
170 }
171 if let Some(ref mut all_dirs) = self.all_dirs {
171 if let Some(ref mut all_dirs) = self.all_dirs {
172 all_dirs.delete_path(filename)?;
172 all_dirs.delete_path(filename)?;
173 }
173 }
174 }
174 }
175 if let Some(ref mut file_fold_map) = self.file_fold_map {
175 if let Some(ref mut file_fold_map) = self.file_fold_map {
176 file_fold_map.remove(&normalize_case(filename));
176 file_fold_map.remove(&normalize_case(filename));
177 }
177 }
178 self.get_non_normal_other_parent_entries()
178 self.get_non_normal_other_parent_entries()
179 .0
179 .0
180 .remove(filename);
180 .remove(filename);
181
181
182 Ok(exists)
182 Ok(exists)
183 }
183 }
184
184
185 pub fn clear_ambiguous_times(
185 pub fn clear_ambiguous_times(
186 &mut self,
186 &mut self,
187 filenames: Vec<HgPathBuf>,
187 filenames: Vec<HgPathBuf>,
188 now: i32,
188 now: i32,
189 ) {
189 ) {
190 for filename in filenames {
190 for filename in filenames {
191 let mut changed = false;
191 let mut changed = false;
192 if let Some(entry) = self.state_map.get_mut(&filename) {
192 if let Some(entry) = self.state_map.get_mut(&filename) {
193 if entry.state == EntryState::Normal && entry.mtime == now {
193 if entry.state == EntryState::Normal && entry.mtime == now {
194 changed = true;
194 changed = true;
195 *entry = DirstateEntry {
195 *entry = DirstateEntry {
196 mtime: MTIME_UNSET,
196 mtime: MTIME_UNSET,
197 ..*entry
197 ..*entry
198 };
198 };
199 }
199 }
200 }
200 }
201 if changed {
201 if changed {
202 self.get_non_normal_other_parent_entries()
202 self.get_non_normal_other_parent_entries()
203 .0
203 .0
204 .insert(filename.to_owned());
204 .insert(filename.to_owned());
205 }
205 }
206 }
206 }
207 }
207 }
208
208
209 pub fn non_normal_entries_remove(
209 pub fn non_normal_entries_remove(
210 &mut self,
210 &mut self,
211 key: impl AsRef<HgPath>,
211 key: impl AsRef<HgPath>,
212 ) -> bool {
212 ) -> bool {
213 self.get_non_normal_other_parent_entries()
213 self.get_non_normal_other_parent_entries()
214 .0
214 .0
215 .remove(key.as_ref())
215 .remove(key.as_ref())
216 }
216 }
217 pub fn non_normal_entries_union(
217 pub fn non_normal_entries_union(
218 &mut self,
218 &mut self,
219 other: HashSet<HgPathBuf>,
219 other: HashSet<HgPathBuf>,
220 ) -> Vec<HgPathBuf> {
220 ) -> Vec<HgPathBuf> {
221 self.get_non_normal_other_parent_entries()
221 self.get_non_normal_other_parent_entries()
222 .0
222 .0
223 .union(&other)
223 .union(&other)
224 .map(ToOwned::to_owned)
224 .map(ToOwned::to_owned)
225 .collect()
225 .collect()
226 }
226 }
227
227
228 pub fn get_non_normal_other_parent_entries(
228 pub fn get_non_normal_other_parent_entries(
229 &mut self,
229 &mut self,
230 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
230 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
231 self.set_non_normal_other_parent_entries(false);
231 self.set_non_normal_other_parent_entries(false);
232 (
232 (
233 self.non_normal_set.as_mut().unwrap(),
233 self.non_normal_set.as_mut().unwrap(),
234 self.other_parent_set.as_mut().unwrap(),
234 self.other_parent_set.as_mut().unwrap(),
235 )
235 )
236 }
236 }
237
237
238 /// Useful to get immutable references to those sets in contexts where
238 /// Useful to get immutable references to those sets in contexts where
239 /// you only have an immutable reference to the `DirstateMap`, like when
239 /// you only have an immutable reference to the `DirstateMap`, like when
240 /// sharing references with Python.
240 /// sharing references with Python.
241 ///
241 ///
242 /// TODO, get rid of this along with the other "setter/getter" stuff when
242 /// TODO, get rid of this along with the other "setter/getter" stuff when
243 /// a nice typestate plan is defined.
243 /// a nice typestate plan is defined.
244 ///
244 ///
245 /// # Panics
245 /// # Panics
246 ///
246 ///
247 /// Will panic if either set is `None`.
247 /// Will panic if either set is `None`.
248 pub fn get_non_normal_other_parent_entries_panic(
248 pub fn get_non_normal_other_parent_entries_panic(
249 &self,
249 &self,
250 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
250 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
251 (
251 (
252 self.non_normal_set.as_ref().unwrap(),
252 self.non_normal_set.as_ref().unwrap(),
253 self.other_parent_set.as_ref().unwrap(),
253 self.other_parent_set.as_ref().unwrap(),
254 )
254 )
255 }
255 }
256
256
257 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
257 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
258 if !force
258 if !force
259 && self.non_normal_set.is_some()
259 && self.non_normal_set.is_some()
260 && self.other_parent_set.is_some()
260 && self.other_parent_set.is_some()
261 {
261 {
262 return;
262 return;
263 }
263 }
264 let mut non_normal = HashSet::new();
264 let mut non_normal = HashSet::new();
265 let mut other_parent = HashSet::new();
265 let mut other_parent = HashSet::new();
266
266
267 for (
267 for (
268 filename,
268 filename,
269 DirstateEntry {
269 DirstateEntry {
270 state, size, mtime, ..
270 state, size, mtime, ..
271 },
271 },
272 ) in self.state_map.iter()
272 ) in self.state_map.iter()
273 {
273 {
274 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
274 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
275 non_normal.insert(filename.to_owned());
275 non_normal.insert(filename.to_owned());
276 }
276 }
277 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
277 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
278 {
278 {
279 other_parent.insert(filename.to_owned());
279 other_parent.insert(filename.to_owned());
280 }
280 }
281 }
281 }
282 self.non_normal_set = Some(non_normal);
282 self.non_normal_set = Some(non_normal);
283 self.other_parent_set = Some(other_parent);
283 self.other_parent_set = Some(other_parent);
284 }
284 }
285
285
286 /// Both of these setters and their uses appear to be the simplest way to
286 /// Both of these setters and their uses appear to be the simplest way to
287 /// emulate a Python lazy property, but it is ugly and unidiomatic.
287 /// emulate a Python lazy property, but it is ugly and unidiomatic.
288 /// TODO One day, rewriting this struct using the typestate might be a
288 /// TODO One day, rewriting this struct using the typestate might be a
289 /// good idea.
289 /// good idea.
290 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
290 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
291 if self.all_dirs.is_none() {
291 if self.all_dirs.is_none() {
292 self.all_dirs = Some(DirsMultiset::from_dirstate(
292 self.all_dirs = Some(DirsMultiset::from_dirstate(
293 self.state_map.iter(),
293 self.state_map.iter(),
294 None,
294 None,
295 )?);
295 )?);
296 }
296 }
297 Ok(())
297 Ok(())
298 }
298 }
299
299
300 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
300 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
301 if self.dirs.is_none() {
301 if self.dirs.is_none() {
302 self.dirs = Some(DirsMultiset::from_dirstate(
302 self.dirs = Some(DirsMultiset::from_dirstate(
303 &self.state_map,
303 &self.state_map,
304 Some(EntryState::Removed),
304 Some(EntryState::Removed),
305 )?);
305 )?);
306 }
306 }
307 Ok(())
307 Ok(())
308 }
308 }
309
309
310 pub fn has_tracked_dir(
310 pub fn has_tracked_dir(
311 &mut self,
311 &mut self,
312 directory: &HgPath,
312 directory: &HgPath,
313 ) -> Result<bool, DirstateMapError> {
313 ) -> Result<bool, DirstateMapError> {
314 self.set_dirs()?;
314 self.set_dirs()?;
315 Ok(self.dirs.as_ref().unwrap().contains(directory))
315 Ok(self.dirs.as_ref().unwrap().contains(directory))
316 }
316 }
317
317
318 pub fn has_dir(
318 pub fn has_dir(
319 &mut self,
319 &mut self,
320 directory: &HgPath,
320 directory: &HgPath,
321 ) -> Result<bool, DirstateMapError> {
321 ) -> Result<bool, DirstateMapError> {
322 self.set_all_dirs()?;
322 self.set_all_dirs()?;
323 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
323 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
324 }
324 }
325
325
326 pub fn parents(
326 pub fn parents(
327 &mut self,
327 &mut self,
328 file_contents: &[u8],
328 file_contents: &[u8],
329 ) -> Result<&DirstateParents, DirstateError> {
329 ) -> Result<&DirstateParents, DirstateError> {
330 if let Some(ref parents) = self.parents {
330 if let Some(ref parents) = self.parents {
331 return Ok(parents);
331 return Ok(parents);
332 }
332 }
333 let parents;
333 let parents;
334 if file_contents.len() == PARENT_SIZE * 2 {
334 if file_contents.len() == PARENT_SIZE * 2 {
335 parents = DirstateParents {
335 parents = DirstateParents {
336 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
336 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
337 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
337 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
338 .try_into()
338 .try_into()
339 .unwrap(),
339 .unwrap(),
340 };
340 };
341 } else if file_contents.is_empty() {
341 } else if file_contents.is_empty() {
342 parents = DirstateParents {
342 parents = DirstateParents {
343 p1: NULL_NODE,
343 p1: NULL_NODE,
344 p2: NULL_NODE,
344 p2: NULL_NODE,
345 };
345 };
346 } else {
346 } else {
347 return Err(
347 return Err(
348 HgError::corrupted("Dirstate appears to be damaged").into()
348 HgError::corrupted("Dirstate appears to be damaged").into()
349 );
349 );
350 }
350 }
351
351
352 self.parents = Some(parents);
352 self.parents = Some(parents);
353 Ok(self.parents.as_ref().unwrap())
353 Ok(self.parents.as_ref().unwrap())
354 }
354 }
355
355
356 pub fn set_parents(&mut self, parents: &DirstateParents) {
356 pub fn set_parents(&mut self, parents: &DirstateParents) {
357 self.parents = Some(parents.clone());
357 self.parents = Some(parents.clone());
358 self.dirty_parents = true;
358 self.dirty_parents = true;
359 }
359 }
360
360
361 #[timed]
361 #[timed]
362 pub fn read<'a>(
362 pub fn read<'a>(
363 &mut self,
363 &mut self,
364 file_contents: &'a [u8],
364 file_contents: &'a [u8],
365 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
365 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
366 if file_contents.is_empty() {
366 if file_contents.is_empty() {
367 return Ok(None);
367 return Ok(None);
368 }
368 }
369
369
370 let (parents, entries, copies) = parse_dirstate(file_contents)?;
370 let (parents, entries, copies) = parse_dirstate(file_contents)?;
371 self.state_map.extend(
371 self.state_map.extend(
372 entries
372 entries
373 .into_iter()
373 .into_iter()
374 .map(|(path, entry)| (path.to_owned(), entry)),
374 .map(|(path, entry)| (path.to_owned(), entry)),
375 );
375 );
376 self.copy_map.extend(
376 self.copy_map.extend(
377 copies
377 copies
378 .into_iter()
378 .into_iter()
379 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
379 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
380 );
380 );
381
381
382 if !self.dirty_parents {
382 if !self.dirty_parents {
383 self.set_parents(&parents);
383 self.set_parents(&parents);
384 }
384 }
385
385
386 Ok(Some(parents))
386 Ok(Some(parents))
387 }
387 }
388
388
389 pub fn pack(
389 pub fn pack(
390 &mut self,
390 &mut self,
391 parents: DirstateParents,
391 parents: DirstateParents,
392 now: Duration,
392 now: Timestamp,
393 ) -> Result<Vec<u8>, DirstateError> {
393 ) -> Result<Vec<u8>, DirstateError> {
394 let packed =
394 let packed =
395 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
395 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
396
396
397 self.dirty_parents = false;
397 self.dirty_parents = false;
398
398
399 self.set_non_normal_other_parent_entries(true);
399 self.set_non_normal_other_parent_entries(true);
400 Ok(packed)
400 Ok(packed)
401 }
401 }
402 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
402 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
403 if let Some(ref file_fold_map) = self.file_fold_map {
403 if let Some(ref file_fold_map) = self.file_fold_map {
404 return file_fold_map;
404 return file_fold_map;
405 }
405 }
406 let mut new_file_fold_map = FileFoldMap::default();
406 let mut new_file_fold_map = FileFoldMap::default();
407
407
408 for (filename, DirstateEntry { state, .. }) in self.state_map.iter() {
408 for (filename, DirstateEntry { state, .. }) in self.state_map.iter() {
409 if *state != EntryState::Removed {
409 if *state != EntryState::Removed {
410 new_file_fold_map
410 new_file_fold_map
411 .insert(normalize_case(&filename), filename.to_owned());
411 .insert(normalize_case(&filename), filename.to_owned());
412 }
412 }
413 }
413 }
414 self.file_fold_map = Some(new_file_fold_map);
414 self.file_fold_map = Some(new_file_fold_map);
415 self.file_fold_map.as_ref().unwrap()
415 self.file_fold_map.as_ref().unwrap()
416 }
416 }
417 }
417 }
418
418
419 #[cfg(test)]
419 #[cfg(test)]
420 mod tests {
420 mod tests {
421 use super::*;
421 use super::*;
422
422
423 #[test]
423 #[test]
424 fn test_dirs_multiset() {
424 fn test_dirs_multiset() {
425 let mut map = DirstateMap::new();
425 let mut map = DirstateMap::new();
426 assert!(map.dirs.is_none());
426 assert!(map.dirs.is_none());
427 assert!(map.all_dirs.is_none());
427 assert!(map.all_dirs.is_none());
428
428
429 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
429 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
430 assert!(map.all_dirs.is_some());
430 assert!(map.all_dirs.is_some());
431 assert!(map.dirs.is_none());
431 assert!(map.dirs.is_none());
432
432
433 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
433 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
434 assert!(map.dirs.is_some());
434 assert!(map.dirs.is_some());
435 }
435 }
436
436
437 #[test]
437 #[test]
438 fn test_add_file() {
438 fn test_add_file() {
439 let mut map = DirstateMap::new();
439 let mut map = DirstateMap::new();
440
440
441 assert_eq!(0, map.len());
441 assert_eq!(0, map.len());
442
442
443 map.add_file(
443 map.add_file(
444 HgPath::new(b"meh"),
444 HgPath::new(b"meh"),
445 EntryState::Normal,
445 EntryState::Normal,
446 DirstateEntry {
446 DirstateEntry {
447 state: EntryState::Normal,
447 state: EntryState::Normal,
448 mode: 1337,
448 mode: 1337,
449 mtime: 1337,
449 mtime: 1337,
450 size: 1337,
450 size: 1337,
451 },
451 },
452 )
452 )
453 .unwrap();
453 .unwrap();
454
454
455 assert_eq!(1, map.len());
455 assert_eq!(1, map.len());
456 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
456 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
457 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
457 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
458 }
458 }
459
459
460 #[test]
460 #[test]
461 fn test_non_normal_other_parent_entries() {
461 fn test_non_normal_other_parent_entries() {
462 let mut map: DirstateMap = [
462 let mut map: DirstateMap = [
463 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
463 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
464 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
464 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
465 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
465 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
466 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
466 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
467 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
467 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
468 (b"f6", (EntryState::Added, 1337, 1337, -1)),
468 (b"f6", (EntryState::Added, 1337, 1337, -1)),
469 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
469 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
470 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
470 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
471 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
471 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
472 (b"fa", (EntryState::Added, 1337, -2, 1337)),
472 (b"fa", (EntryState::Added, 1337, -2, 1337)),
473 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
473 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
474 ]
474 ]
475 .iter()
475 .iter()
476 .map(|(fname, (state, mode, size, mtime))| {
476 .map(|(fname, (state, mode, size, mtime))| {
477 (
477 (
478 HgPathBuf::from_bytes(fname.as_ref()),
478 HgPathBuf::from_bytes(fname.as_ref()),
479 DirstateEntry {
479 DirstateEntry {
480 state: *state,
480 state: *state,
481 mode: *mode,
481 mode: *mode,
482 size: *size,
482 size: *size,
483 mtime: *mtime,
483 mtime: *mtime,
484 },
484 },
485 )
485 )
486 })
486 })
487 .collect();
487 .collect();
488
488
489 let mut non_normal = [
489 let mut non_normal = [
490 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
490 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
491 ]
491 ]
492 .iter()
492 .iter()
493 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
493 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
494 .collect();
494 .collect();
495
495
496 let mut other_parent = HashSet::new();
496 let mut other_parent = HashSet::new();
497 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
497 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
498 let entries = map.get_non_normal_other_parent_entries();
498 let entries = map.get_non_normal_other_parent_entries();
499
499
500 assert_eq!(
500 assert_eq!(
501 (&mut non_normal, &mut other_parent),
501 (&mut non_normal, &mut other_parent),
502 (entries.0, entries.1)
502 (entries.0, entries.1)
503 );
503 );
504 }
504 }
505 }
505 }
@@ -1,445 +1,446
1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
2 //
2 //
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 use crate::errors::HgError;
6 use crate::errors::HgError;
7 use crate::utils::hg_path::HgPath;
7 use crate::utils::hg_path::HgPath;
8 use crate::{
8 use crate::{
9 dirstate::{CopyMap, EntryState, RawEntry, StateMap},
9 dirstate::{CopyMap, EntryState, RawEntry, StateMap},
10 DirstateEntry, DirstateParents,
10 DirstateEntry, DirstateParents,
11 };
11 };
12 use byteorder::{BigEndian, WriteBytesExt};
12 use byteorder::{BigEndian, WriteBytesExt};
13 use bytes_cast::BytesCast;
13 use bytes_cast::BytesCast;
14 use micro_timer::timed;
14 use micro_timer::timed;
15 use std::convert::{TryFrom, TryInto};
15 use std::convert::{TryFrom, TryInto};
16 use std::time::Duration;
17
16
18 /// Parents are stored in the dirstate as byte hashes.
17 /// Parents are stored in the dirstate as byte hashes.
19 pub const PARENT_SIZE: usize = 20;
18 pub const PARENT_SIZE: usize = 20;
20 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
19 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
21 const MIN_ENTRY_SIZE: usize = 17;
20 const MIN_ENTRY_SIZE: usize = 17;
22
21
23 type ParseResult<'a> = (
22 type ParseResult<'a> = (
24 &'a DirstateParents,
23 &'a DirstateParents,
25 Vec<(&'a HgPath, DirstateEntry)>,
24 Vec<(&'a HgPath, DirstateEntry)>,
26 Vec<(&'a HgPath, &'a HgPath)>,
25 Vec<(&'a HgPath, &'a HgPath)>,
27 );
26 );
28
27
29 pub fn parse_dirstate_parents(
28 pub fn parse_dirstate_parents(
30 contents: &[u8],
29 contents: &[u8],
31 ) -> Result<&DirstateParents, HgError> {
30 ) -> Result<&DirstateParents, HgError> {
32 let (parents, _rest) = DirstateParents::from_bytes(contents)
31 let (parents, _rest) = DirstateParents::from_bytes(contents)
33 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
32 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
34 Ok(parents)
33 Ok(parents)
35 }
34 }
36
35
37 #[timed]
36 #[timed]
38 pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
37 pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
39 let mut copies = Vec::new();
38 let mut copies = Vec::new();
40 let mut entries = Vec::new();
39 let mut entries = Vec::new();
41 let parents =
40 let parents =
42 parse_dirstate_entries(contents, |path, entry, copy_source| {
41 parse_dirstate_entries(contents, |path, entry, copy_source| {
43 if let Some(source) = copy_source {
42 if let Some(source) = copy_source {
44 copies.push((path, source));
43 copies.push((path, source));
45 }
44 }
46 entries.push((path, *entry));
45 entries.push((path, *entry));
47 })?;
46 })?;
48 Ok((parents, entries, copies))
47 Ok((parents, entries, copies))
49 }
48 }
50
49
51 pub fn parse_dirstate_entries<'a>(
50 pub fn parse_dirstate_entries<'a>(
52 mut contents: &'a [u8],
51 mut contents: &'a [u8],
53 mut each_entry: impl FnMut(&'a HgPath, &DirstateEntry, Option<&'a HgPath>),
52 mut each_entry: impl FnMut(&'a HgPath, &DirstateEntry, Option<&'a HgPath>),
54 ) -> Result<&'a DirstateParents, HgError> {
53 ) -> Result<&'a DirstateParents, HgError> {
55 let (parents, rest) = DirstateParents::from_bytes(contents)
54 let (parents, rest) = DirstateParents::from_bytes(contents)
56 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
55 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
57 contents = rest;
56 contents = rest;
58 while !contents.is_empty() {
57 while !contents.is_empty() {
59 let (raw_entry, rest) = RawEntry::from_bytes(contents)
58 let (raw_entry, rest) = RawEntry::from_bytes(contents)
60 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
59 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
61
60
62 let entry = DirstateEntry {
61 let entry = DirstateEntry {
63 state: EntryState::try_from(raw_entry.state)?,
62 state: EntryState::try_from(raw_entry.state)?,
64 mode: raw_entry.mode.get(),
63 mode: raw_entry.mode.get(),
65 mtime: raw_entry.mtime.get(),
64 mtime: raw_entry.mtime.get(),
66 size: raw_entry.size.get(),
65 size: raw_entry.size.get(),
67 };
66 };
68 let (paths, rest) =
67 let (paths, rest) =
69 u8::slice_from_bytes(rest, raw_entry.length.get() as usize)
68 u8::slice_from_bytes(rest, raw_entry.length.get() as usize)
70 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
69 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
71
70
72 // `paths` is either a single path, or two paths separated by a NULL
71 // `paths` is either a single path, or two paths separated by a NULL
73 // byte
72 // byte
74 let mut iter = paths.splitn(2, |&byte| byte == b'\0');
73 let mut iter = paths.splitn(2, |&byte| byte == b'\0');
75 let path = HgPath::new(
74 let path = HgPath::new(
76 iter.next().expect("splitn always yields at least one item"),
75 iter.next().expect("splitn always yields at least one item"),
77 );
76 );
78 let copy_source = iter.next().map(HgPath::new);
77 let copy_source = iter.next().map(HgPath::new);
79 each_entry(path, &entry, copy_source);
78 each_entry(path, &entry, copy_source);
80
79
81 contents = rest;
80 contents = rest;
82 }
81 }
83 Ok(parents)
82 Ok(parents)
84 }
83 }
85
84
86 /// `now` is the duration in seconds since the Unix epoch
85 /// Seconds since the Unix epoch
86 pub struct Timestamp(pub u64);
87
87 pub fn pack_dirstate(
88 pub fn pack_dirstate(
88 state_map: &mut StateMap,
89 state_map: &mut StateMap,
89 copy_map: &CopyMap,
90 copy_map: &CopyMap,
90 parents: DirstateParents,
91 parents: DirstateParents,
91 now: Duration,
92 now: Timestamp,
92 ) -> Result<Vec<u8>, HgError> {
93 ) -> Result<Vec<u8>, HgError> {
93 // TODO move away from i32 before 2038.
94 // TODO move away from i32 before 2038.
94 let now: i32 = now.as_secs().try_into().expect("time overflow");
95 let now: i32 = now.0.try_into().expect("time overflow");
95
96
96 let expected_size: usize = state_map
97 let expected_size: usize = state_map
97 .iter()
98 .iter()
98 .map(|(filename, _)| {
99 .map(|(filename, _)| {
99 let mut length = MIN_ENTRY_SIZE + filename.len();
100 let mut length = MIN_ENTRY_SIZE + filename.len();
100 if let Some(copy) = copy_map.get(filename) {
101 if let Some(copy) = copy_map.get(filename) {
101 length += copy.len() + 1;
102 length += copy.len() + 1;
102 }
103 }
103 length
104 length
104 })
105 })
105 .sum();
106 .sum();
106 let expected_size = expected_size + PARENT_SIZE * 2;
107 let expected_size = expected_size + PARENT_SIZE * 2;
107
108
108 let mut packed = Vec::with_capacity(expected_size);
109 let mut packed = Vec::with_capacity(expected_size);
109
110
110 packed.extend(parents.p1.as_bytes());
111 packed.extend(parents.p1.as_bytes());
111 packed.extend(parents.p2.as_bytes());
112 packed.extend(parents.p2.as_bytes());
112
113
113 for (filename, entry) in state_map.iter_mut() {
114 for (filename, entry) in state_map.iter_mut() {
114 let new_filename = filename.to_owned();
115 let new_filename = filename.to_owned();
115 let mut new_mtime: i32 = entry.mtime;
116 let mut new_mtime: i32 = entry.mtime;
116 if entry.state == EntryState::Normal && entry.mtime == now {
117 if entry.state == EntryState::Normal && entry.mtime == now {
117 // The file was last modified "simultaneously" with the current
118 // The file was last modified "simultaneously" with the current
118 // write to dirstate (i.e. within the same second for file-
119 // write to dirstate (i.e. within the same second for file-
119 // systems with a granularity of 1 sec). This commonly happens
120 // systems with a granularity of 1 sec). This commonly happens
120 // for at least a couple of files on 'update'.
121 // for at least a couple of files on 'update'.
121 // The user could change the file without changing its size
122 // The user could change the file without changing its size
122 // within the same second. Invalidate the file's mtime in
123 // within the same second. Invalidate the file's mtime in
123 // dirstate, forcing future 'status' calls to compare the
124 // dirstate, forcing future 'status' calls to compare the
124 // contents of the file if the size is the same. This prevents
125 // contents of the file if the size is the same. This prevents
125 // mistakenly treating such files as clean.
126 // mistakenly treating such files as clean.
126 new_mtime = -1;
127 new_mtime = -1;
127 *entry = DirstateEntry {
128 *entry = DirstateEntry {
128 mtime: new_mtime,
129 mtime: new_mtime,
129 ..*entry
130 ..*entry
130 };
131 };
131 }
132 }
132 let mut new_filename = new_filename.into_vec();
133 let mut new_filename = new_filename.into_vec();
133 if let Some(copy) = copy_map.get(filename) {
134 if let Some(copy) = copy_map.get(filename) {
134 new_filename.push(b'\0');
135 new_filename.push(b'\0');
135 new_filename.extend(copy.bytes());
136 new_filename.extend(copy.bytes());
136 }
137 }
137
138
138 // Unwrapping because `impl std::io::Write for Vec<u8>` never errors
139 // Unwrapping because `impl std::io::Write for Vec<u8>` never errors
139 packed.write_u8(entry.state.into()).unwrap();
140 packed.write_u8(entry.state.into()).unwrap();
140 packed.write_i32::<BigEndian>(entry.mode).unwrap();
141 packed.write_i32::<BigEndian>(entry.mode).unwrap();
141 packed.write_i32::<BigEndian>(entry.size).unwrap();
142 packed.write_i32::<BigEndian>(entry.size).unwrap();
142 packed.write_i32::<BigEndian>(new_mtime).unwrap();
143 packed.write_i32::<BigEndian>(new_mtime).unwrap();
143 packed
144 packed
144 .write_i32::<BigEndian>(new_filename.len() as i32)
145 .write_i32::<BigEndian>(new_filename.len() as i32)
145 .unwrap();
146 .unwrap();
146 packed.extend(new_filename)
147 packed.extend(new_filename)
147 }
148 }
148
149
149 if packed.len() != expected_size {
150 if packed.len() != expected_size {
150 return Err(HgError::CorruptedRepository(format!(
151 return Err(HgError::CorruptedRepository(format!(
151 "bad dirstate size: {} != {}",
152 "bad dirstate size: {} != {}",
152 expected_size,
153 expected_size,
153 packed.len()
154 packed.len()
154 )));
155 )));
155 }
156 }
156
157
157 Ok(packed)
158 Ok(packed)
158 }
159 }
159
160
160 #[cfg(test)]
161 #[cfg(test)]
161 mod tests {
162 mod tests {
162 use super::*;
163 use super::*;
163 use crate::{utils::hg_path::HgPathBuf, FastHashMap};
164 use crate::{utils::hg_path::HgPathBuf, FastHashMap};
164 use pretty_assertions::assert_eq;
165 use pretty_assertions::assert_eq;
165
166
166 #[test]
167 #[test]
167 fn test_pack_dirstate_empty() {
168 fn test_pack_dirstate_empty() {
168 let mut state_map = StateMap::default();
169 let mut state_map = StateMap::default();
169 let copymap = FastHashMap::default();
170 let copymap = FastHashMap::default();
170 let parents = DirstateParents {
171 let parents = DirstateParents {
171 p1: b"12345678910111213141".into(),
172 p1: b"12345678910111213141".into(),
172 p2: b"00000000000000000000".into(),
173 p2: b"00000000000000000000".into(),
173 };
174 };
174 let now = Duration::new(15000000, 0);
175 let now = Timestamp(15000000);
175 let expected = b"1234567891011121314100000000000000000000".to_vec();
176 let expected = b"1234567891011121314100000000000000000000".to_vec();
176
177
177 assert_eq!(
178 assert_eq!(
178 expected,
179 expected,
179 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
180 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
180 );
181 );
181
182
182 assert!(state_map.is_empty())
183 assert!(state_map.is_empty())
183 }
184 }
184 #[test]
185 #[test]
185 fn test_pack_dirstate_one_entry() {
186 fn test_pack_dirstate_one_entry() {
186 let expected_state_map: StateMap = [(
187 let expected_state_map: StateMap = [(
187 HgPathBuf::from_bytes(b"f1"),
188 HgPathBuf::from_bytes(b"f1"),
188 DirstateEntry {
189 DirstateEntry {
189 state: EntryState::Normal,
190 state: EntryState::Normal,
190 mode: 0o644,
191 mode: 0o644,
191 size: 0,
192 size: 0,
192 mtime: 791231220,
193 mtime: 791231220,
193 },
194 },
194 )]
195 )]
195 .iter()
196 .iter()
196 .cloned()
197 .cloned()
197 .collect();
198 .collect();
198 let mut state_map = expected_state_map.clone();
199 let mut state_map = expected_state_map.clone();
199
200
200 let copymap = FastHashMap::default();
201 let copymap = FastHashMap::default();
201 let parents = DirstateParents {
202 let parents = DirstateParents {
202 p1: b"12345678910111213141".into(),
203 p1: b"12345678910111213141".into(),
203 p2: b"00000000000000000000".into(),
204 p2: b"00000000000000000000".into(),
204 };
205 };
205 let now = Duration::new(15000000, 0);
206 let now = Timestamp(15000000);
206 let expected = [
207 let expected = [
207 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
208 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
208 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
209 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
209 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
210 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
210 41, 58, 244, 0, 0, 0, 2, 102, 49,
211 41, 58, 244, 0, 0, 0, 2, 102, 49,
211 ]
212 ]
212 .to_vec();
213 .to_vec();
213
214
214 assert_eq!(
215 assert_eq!(
215 expected,
216 expected,
216 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
217 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
217 );
218 );
218
219
219 assert_eq!(expected_state_map, state_map);
220 assert_eq!(expected_state_map, state_map);
220 }
221 }
221 #[test]
222 #[test]
222 fn test_pack_dirstate_one_entry_with_copy() {
223 fn test_pack_dirstate_one_entry_with_copy() {
223 let expected_state_map: StateMap = [(
224 let expected_state_map: StateMap = [(
224 HgPathBuf::from_bytes(b"f1"),
225 HgPathBuf::from_bytes(b"f1"),
225 DirstateEntry {
226 DirstateEntry {
226 state: EntryState::Normal,
227 state: EntryState::Normal,
227 mode: 0o644,
228 mode: 0o644,
228 size: 0,
229 size: 0,
229 mtime: 791231220,
230 mtime: 791231220,
230 },
231 },
231 )]
232 )]
232 .iter()
233 .iter()
233 .cloned()
234 .cloned()
234 .collect();
235 .collect();
235 let mut state_map = expected_state_map.clone();
236 let mut state_map = expected_state_map.clone();
236 let mut copymap = FastHashMap::default();
237 let mut copymap = FastHashMap::default();
237 copymap.insert(
238 copymap.insert(
238 HgPathBuf::from_bytes(b"f1"),
239 HgPathBuf::from_bytes(b"f1"),
239 HgPathBuf::from_bytes(b"copyname"),
240 HgPathBuf::from_bytes(b"copyname"),
240 );
241 );
241 let parents = DirstateParents {
242 let parents = DirstateParents {
242 p1: b"12345678910111213141".into(),
243 p1: b"12345678910111213141".into(),
243 p2: b"00000000000000000000".into(),
244 p2: b"00000000000000000000".into(),
244 };
245 };
245 let now = Duration::new(15000000, 0);
246 let now = Timestamp(15000000);
246 let expected = [
247 let expected = [
247 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
248 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
248 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
249 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
249 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
250 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
250 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
251 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
251 109, 101,
252 109, 101,
252 ]
253 ]
253 .to_vec();
254 .to_vec();
254
255
255 assert_eq!(
256 assert_eq!(
256 expected,
257 expected,
257 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
258 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
258 );
259 );
259 assert_eq!(expected_state_map, state_map);
260 assert_eq!(expected_state_map, state_map);
260 }
261 }
261
262
262 #[test]
263 #[test]
263 fn test_parse_pack_one_entry_with_copy() {
264 fn test_parse_pack_one_entry_with_copy() {
264 let mut state_map: StateMap = [(
265 let mut state_map: StateMap = [(
265 HgPathBuf::from_bytes(b"f1"),
266 HgPathBuf::from_bytes(b"f1"),
266 DirstateEntry {
267 DirstateEntry {
267 state: EntryState::Normal,
268 state: EntryState::Normal,
268 mode: 0o644,
269 mode: 0o644,
269 size: 0,
270 size: 0,
270 mtime: 791231220,
271 mtime: 791231220,
271 },
272 },
272 )]
273 )]
273 .iter()
274 .iter()
274 .cloned()
275 .cloned()
275 .collect();
276 .collect();
276 let mut copymap = FastHashMap::default();
277 let mut copymap = FastHashMap::default();
277 copymap.insert(
278 copymap.insert(
278 HgPathBuf::from_bytes(b"f1"),
279 HgPathBuf::from_bytes(b"f1"),
279 HgPathBuf::from_bytes(b"copyname"),
280 HgPathBuf::from_bytes(b"copyname"),
280 );
281 );
281 let parents = DirstateParents {
282 let parents = DirstateParents {
282 p1: b"12345678910111213141".into(),
283 p1: b"12345678910111213141".into(),
283 p2: b"00000000000000000000".into(),
284 p2: b"00000000000000000000".into(),
284 };
285 };
285 let now = Duration::new(15000000, 0);
286 let now = Timestamp(15000000);
286 let result =
287 let result =
287 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
288 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
288 .unwrap();
289 .unwrap();
289
290
290 let (new_parents, entries, copies) =
291 let (new_parents, entries, copies) =
291 parse_dirstate(result.as_slice()).unwrap();
292 parse_dirstate(result.as_slice()).unwrap();
292 let new_state_map: StateMap = entries
293 let new_state_map: StateMap = entries
293 .into_iter()
294 .into_iter()
294 .map(|(path, entry)| (path.to_owned(), entry))
295 .map(|(path, entry)| (path.to_owned(), entry))
295 .collect();
296 .collect();
296 let new_copy_map: CopyMap = copies
297 let new_copy_map: CopyMap = copies
297 .into_iter()
298 .into_iter()
298 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
299 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
299 .collect();
300 .collect();
300
301
301 assert_eq!(
302 assert_eq!(
302 (&parents, state_map, copymap),
303 (&parents, state_map, copymap),
303 (new_parents, new_state_map, new_copy_map)
304 (new_parents, new_state_map, new_copy_map)
304 )
305 )
305 }
306 }
306
307
307 #[test]
308 #[test]
308 fn test_parse_pack_multiple_entries_with_copy() {
309 fn test_parse_pack_multiple_entries_with_copy() {
309 let mut state_map: StateMap = [
310 let mut state_map: StateMap = [
310 (
311 (
311 HgPathBuf::from_bytes(b"f1"),
312 HgPathBuf::from_bytes(b"f1"),
312 DirstateEntry {
313 DirstateEntry {
313 state: EntryState::Normal,
314 state: EntryState::Normal,
314 mode: 0o644,
315 mode: 0o644,
315 size: 0,
316 size: 0,
316 mtime: 791231220,
317 mtime: 791231220,
317 },
318 },
318 ),
319 ),
319 (
320 (
320 HgPathBuf::from_bytes(b"f2"),
321 HgPathBuf::from_bytes(b"f2"),
321 DirstateEntry {
322 DirstateEntry {
322 state: EntryState::Merged,
323 state: EntryState::Merged,
323 mode: 0o777,
324 mode: 0o777,
324 size: 1000,
325 size: 1000,
325 mtime: 791231220,
326 mtime: 791231220,
326 },
327 },
327 ),
328 ),
328 (
329 (
329 HgPathBuf::from_bytes(b"f3"),
330 HgPathBuf::from_bytes(b"f3"),
330 DirstateEntry {
331 DirstateEntry {
331 state: EntryState::Removed,
332 state: EntryState::Removed,
332 mode: 0o644,
333 mode: 0o644,
333 size: 234553,
334 size: 234553,
334 mtime: 791231220,
335 mtime: 791231220,
335 },
336 },
336 ),
337 ),
337 (
338 (
338 HgPathBuf::from_bytes(b"f4\xF6"),
339 HgPathBuf::from_bytes(b"f4\xF6"),
339 DirstateEntry {
340 DirstateEntry {
340 state: EntryState::Added,
341 state: EntryState::Added,
341 mode: 0o644,
342 mode: 0o644,
342 size: -1,
343 size: -1,
343 mtime: -1,
344 mtime: -1,
344 },
345 },
345 ),
346 ),
346 ]
347 ]
347 .iter()
348 .iter()
348 .cloned()
349 .cloned()
349 .collect();
350 .collect();
350 let mut copymap = FastHashMap::default();
351 let mut copymap = FastHashMap::default();
351 copymap.insert(
352 copymap.insert(
352 HgPathBuf::from_bytes(b"f1"),
353 HgPathBuf::from_bytes(b"f1"),
353 HgPathBuf::from_bytes(b"copyname"),
354 HgPathBuf::from_bytes(b"copyname"),
354 );
355 );
355 copymap.insert(
356 copymap.insert(
356 HgPathBuf::from_bytes(b"f4\xF6"),
357 HgPathBuf::from_bytes(b"f4\xF6"),
357 HgPathBuf::from_bytes(b"copyname2"),
358 HgPathBuf::from_bytes(b"copyname2"),
358 );
359 );
359 let parents = DirstateParents {
360 let parents = DirstateParents {
360 p1: b"12345678910111213141".into(),
361 p1: b"12345678910111213141".into(),
361 p2: b"00000000000000000000".into(),
362 p2: b"00000000000000000000".into(),
362 };
363 };
363 let now = Duration::new(15000000, 0);
364 let now = Timestamp(15000000);
364 let result =
365 let result =
365 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
366 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
366 .unwrap();
367 .unwrap();
367
368
368 let (new_parents, entries, copies) =
369 let (new_parents, entries, copies) =
369 parse_dirstate(result.as_slice()).unwrap();
370 parse_dirstate(result.as_slice()).unwrap();
370 let new_state_map: StateMap = entries
371 let new_state_map: StateMap = entries
371 .into_iter()
372 .into_iter()
372 .map(|(path, entry)| (path.to_owned(), entry))
373 .map(|(path, entry)| (path.to_owned(), entry))
373 .collect();
374 .collect();
374 let new_copy_map: CopyMap = copies
375 let new_copy_map: CopyMap = copies
375 .into_iter()
376 .into_iter()
376 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
377 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
377 .collect();
378 .collect();
378
379
379 assert_eq!(
380 assert_eq!(
380 (&parents, state_map, copymap),
381 (&parents, state_map, copymap),
381 (new_parents, new_state_map, new_copy_map)
382 (new_parents, new_state_map, new_copy_map)
382 )
383 )
383 }
384 }
384
385
385 #[test]
386 #[test]
386 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
387 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
387 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
388 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
388 let mut state_map: StateMap = [(
389 let mut state_map: StateMap = [(
389 HgPathBuf::from_bytes(b"f1"),
390 HgPathBuf::from_bytes(b"f1"),
390 DirstateEntry {
391 DirstateEntry {
391 state: EntryState::Normal,
392 state: EntryState::Normal,
392 mode: 0o644,
393 mode: 0o644,
393 size: 0,
394 size: 0,
394 mtime: 15000000,
395 mtime: 15000000,
395 },
396 },
396 )]
397 )]
397 .iter()
398 .iter()
398 .cloned()
399 .cloned()
399 .collect();
400 .collect();
400 let mut copymap = FastHashMap::default();
401 let mut copymap = FastHashMap::default();
401 copymap.insert(
402 copymap.insert(
402 HgPathBuf::from_bytes(b"f1"),
403 HgPathBuf::from_bytes(b"f1"),
403 HgPathBuf::from_bytes(b"copyname"),
404 HgPathBuf::from_bytes(b"copyname"),
404 );
405 );
405 let parents = DirstateParents {
406 let parents = DirstateParents {
406 p1: b"12345678910111213141".into(),
407 p1: b"12345678910111213141".into(),
407 p2: b"00000000000000000000".into(),
408 p2: b"00000000000000000000".into(),
408 };
409 };
409 let now = Duration::new(15000000, 0);
410 let now = Timestamp(15000000);
410 let result =
411 let result =
411 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
412 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
412 .unwrap();
413 .unwrap();
413
414
414 let (new_parents, entries, copies) =
415 let (new_parents, entries, copies) =
415 parse_dirstate(result.as_slice()).unwrap();
416 parse_dirstate(result.as_slice()).unwrap();
416 let new_state_map: StateMap = entries
417 let new_state_map: StateMap = entries
417 .into_iter()
418 .into_iter()
418 .map(|(path, entry)| (path.to_owned(), entry))
419 .map(|(path, entry)| (path.to_owned(), entry))
419 .collect();
420 .collect();
420 let new_copy_map: CopyMap = copies
421 let new_copy_map: CopyMap = copies
421 .into_iter()
422 .into_iter()
422 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
423 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
423 .collect();
424 .collect();
424
425
425 assert_eq!(
426 assert_eq!(
426 (
427 (
427 &parents,
428 &parents,
428 [(
429 [(
429 HgPathBuf::from_bytes(b"f1"),
430 HgPathBuf::from_bytes(b"f1"),
430 DirstateEntry {
431 DirstateEntry {
431 state: EntryState::Normal,
432 state: EntryState::Normal,
432 mode: 0o644,
433 mode: 0o644,
433 size: 0,
434 size: 0,
434 mtime: -1
435 mtime: -1
435 }
436 }
436 )]
437 )]
437 .iter()
438 .iter()
438 .cloned()
439 .cloned()
439 .collect::<StateMap>(),
440 .collect::<StateMap>(),
440 copymap,
441 copymap,
441 ),
442 ),
442 (new_parents, new_state_map, new_copy_map)
443 (new_parents, new_state_map, new_copy_map)
443 )
444 )
444 }
445 }
445 }
446 }
@@ -1,418 +1,418
1 use std::collections::BTreeMap;
1 use std::collections::BTreeMap;
2 use std::path::PathBuf;
2 use std::path::PathBuf;
3 use std::time::Duration;
4
3
5 use super::path_with_basename::WithBasename;
4 use super::path_with_basename::WithBasename;
6 use crate::dirstate::parsers::parse_dirstate_entries;
5 use crate::dirstate::parsers::parse_dirstate_entries;
7 use crate::dirstate::parsers::parse_dirstate_parents;
6 use crate::dirstate::parsers::parse_dirstate_parents;
7 use crate::dirstate::parsers::Timestamp;
8
8
9 use crate::matchers::Matcher;
9 use crate::matchers::Matcher;
10 use crate::revlog::node::NULL_NODE;
10 use crate::revlog::node::NULL_NODE;
11 use crate::utils::hg_path::{HgPath, HgPathBuf};
11 use crate::utils::hg_path::{HgPath, HgPathBuf};
12 use crate::CopyMapIter;
12 use crate::CopyMapIter;
13 use crate::DirstateEntry;
13 use crate::DirstateEntry;
14 use crate::DirstateError;
14 use crate::DirstateError;
15 use crate::DirstateMapError;
15 use crate::DirstateMapError;
16 use crate::DirstateParents;
16 use crate::DirstateParents;
17 use crate::DirstateStatus;
17 use crate::DirstateStatus;
18 use crate::EntryState;
18 use crate::EntryState;
19 use crate::FastHashMap;
19 use crate::FastHashMap;
20 use crate::HgPathCow;
20 use crate::HgPathCow;
21 use crate::PatternFileWarning;
21 use crate::PatternFileWarning;
22 use crate::StateMapIter;
22 use crate::StateMapIter;
23 use crate::StatusError;
23 use crate::StatusError;
24 use crate::StatusOptions;
24 use crate::StatusOptions;
25
25
26 pub struct DirstateMap {
26 pub struct DirstateMap {
27 parents: Option<DirstateParents>,
27 parents: Option<DirstateParents>,
28 dirty_parents: bool,
28 dirty_parents: bool,
29 root: ChildNodes,
29 root: ChildNodes,
30 }
30 }
31
31
32 /// Using a plain `HgPathBuf` of the full path from the repository root as a
32 /// Using a plain `HgPathBuf` of the full path from the repository root as a
33 /// map key would also work: all paths in a given map have the same parent
33 /// map key would also work: all paths in a given map have the same parent
34 /// path, so comparing full paths gives the same result as comparing base
34 /// path, so comparing full paths gives the same result as comparing base
35 /// names. However `BTreeMap` would waste time always re-comparing the same
35 /// names. However `BTreeMap` would waste time always re-comparing the same
36 /// string prefix.
36 /// string prefix.
37 type ChildNodes = BTreeMap<WithBasename<HgPathBuf>, Node>;
37 type ChildNodes = BTreeMap<WithBasename<HgPathBuf>, Node>;
38
38
39 #[derive(Default)]
39 #[derive(Default)]
40 struct Node {
40 struct Node {
41 entry: Option<DirstateEntry>,
41 entry: Option<DirstateEntry>,
42 copy_source: Option<HgPathBuf>,
42 copy_source: Option<HgPathBuf>,
43 children: ChildNodes,
43 children: ChildNodes,
44 }
44 }
45
45
46 /// `(full_path, entry, copy_source)`
46 /// `(full_path, entry, copy_source)`
47 type NodeDataMut<'a> = (
47 type NodeDataMut<'a> = (
48 &'a WithBasename<HgPathBuf>,
48 &'a WithBasename<HgPathBuf>,
49 &'a mut Option<DirstateEntry>,
49 &'a mut Option<DirstateEntry>,
50 &'a mut Option<HgPathBuf>,
50 &'a mut Option<HgPathBuf>,
51 );
51 );
52
52
53 impl DirstateMap {
53 impl DirstateMap {
54 pub fn new() -> Self {
54 pub fn new() -> Self {
55 Self {
55 Self {
56 parents: None,
56 parents: None,
57 dirty_parents: false,
57 dirty_parents: false,
58 root: ChildNodes::new(),
58 root: ChildNodes::new(),
59 }
59 }
60 }
60 }
61
61
62 fn get_node(&self, path: &HgPath) -> Option<&Node> {
62 fn get_node(&self, path: &HgPath) -> Option<&Node> {
63 let mut children = &self.root;
63 let mut children = &self.root;
64 let mut components = path.components();
64 let mut components = path.components();
65 let mut component =
65 let mut component =
66 components.next().expect("expected at least one components");
66 components.next().expect("expected at least one components");
67 loop {
67 loop {
68 let child = children.get(component)?;
68 let child = children.get(component)?;
69 if let Some(next_component) = components.next() {
69 if let Some(next_component) = components.next() {
70 component = next_component;
70 component = next_component;
71 children = &child.children;
71 children = &child.children;
72 } else {
72 } else {
73 return Some(child);
73 return Some(child);
74 }
74 }
75 }
75 }
76 }
76 }
77
77
78 fn get_or_insert_node(&mut self, path: &HgPath) -> &mut Node {
78 fn get_or_insert_node(&mut self, path: &HgPath) -> &mut Node {
79 let mut child_nodes = &mut self.root;
79 let mut child_nodes = &mut self.root;
80 let mut inclusive_ancestor_paths =
80 let mut inclusive_ancestor_paths =
81 WithBasename::inclusive_ancestors_of(path);
81 WithBasename::inclusive_ancestors_of(path);
82 let mut ancestor_path = inclusive_ancestor_paths
82 let mut ancestor_path = inclusive_ancestor_paths
83 .next()
83 .next()
84 .expect("expected at least one inclusive ancestor");
84 .expect("expected at least one inclusive ancestor");
85 loop {
85 loop {
86 // TODO: can we avoid double lookup in all cases without allocating
86 // TODO: can we avoid double lookup in all cases without allocating
87 // an owned key in cases where the map already contains that key?
87 // an owned key in cases where the map already contains that key?
88 let child_node =
88 let child_node =
89 if child_nodes.contains_key(ancestor_path.base_name()) {
89 if child_nodes.contains_key(ancestor_path.base_name()) {
90 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
90 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
91 } else {
91 } else {
92 // This is always a vacant entry, using `.entry()` lets us
92 // This is always a vacant entry, using `.entry()` lets us
93 // return a `&mut Node` of the newly-inserted node without
93 // return a `&mut Node` of the newly-inserted node without
94 // yet another lookup. `BTreeMap::insert` doesn’t do this.
94 // yet another lookup. `BTreeMap::insert` doesn’t do this.
95 child_nodes.entry(ancestor_path.to_owned()).or_default()
95 child_nodes.entry(ancestor_path.to_owned()).or_default()
96 };
96 };
97 if let Some(next) = inclusive_ancestor_paths.next() {
97 if let Some(next) = inclusive_ancestor_paths.next() {
98 ancestor_path = next;
98 ancestor_path = next;
99 child_nodes = &mut child_node.children;
99 child_nodes = &mut child_node.children;
100 } else {
100 } else {
101 return child_node;
101 return child_node;
102 }
102 }
103 }
103 }
104 }
104 }
105
105
106 fn iter_nodes<'a>(
106 fn iter_nodes<'a>(
107 &'a self,
107 &'a self,
108 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
108 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
109 {
109 {
110 // Depth first tree traversal.
110 // Depth first tree traversal.
111 //
111 //
112 // If we could afford internal iteration and recursion,
112 // If we could afford internal iteration and recursion,
113 // this would look like:
113 // this would look like:
114 //
114 //
115 // ```
115 // ```
116 // fn traverse_children(
116 // fn traverse_children(
117 // children: &ChildNodes,
117 // children: &ChildNodes,
118 // each: &mut impl FnMut(&Node),
118 // each: &mut impl FnMut(&Node),
119 // ) {
119 // ) {
120 // for child in children.values() {
120 // for child in children.values() {
121 // traverse_children(&child.children, each);
121 // traverse_children(&child.children, each);
122 // each(child);
122 // each(child);
123 // }
123 // }
124 // }
124 // }
125 // ```
125 // ```
126 //
126 //
127 // However we want an external iterator and therefore can’t use the
127 // However we want an external iterator and therefore can’t use the
128 // call stack. Use an explicit stack instead:
128 // call stack. Use an explicit stack instead:
129 let mut stack = Vec::new();
129 let mut stack = Vec::new();
130 let mut iter = self.root.iter();
130 let mut iter = self.root.iter();
131 std::iter::from_fn(move || {
131 std::iter::from_fn(move || {
132 while let Some((key, child_node)) = iter.next() {
132 while let Some((key, child_node)) = iter.next() {
133 // Pseudo-recursion
133 // Pseudo-recursion
134 let new_iter = child_node.children.iter();
134 let new_iter = child_node.children.iter();
135 let old_iter = std::mem::replace(&mut iter, new_iter);
135 let old_iter = std::mem::replace(&mut iter, new_iter);
136 stack.push((key, child_node, old_iter));
136 stack.push((key, child_node, old_iter));
137 }
137 }
138 // Found the end of a `children.iter()` iterator.
138 // Found the end of a `children.iter()` iterator.
139 if let Some((key, child_node, next_iter)) = stack.pop() {
139 if let Some((key, child_node, next_iter)) = stack.pop() {
140 // "Return" from pseudo-recursion by restoring state from the
140 // "Return" from pseudo-recursion by restoring state from the
141 // explicit stack
141 // explicit stack
142 iter = next_iter;
142 iter = next_iter;
143
143
144 Some((key, child_node))
144 Some((key, child_node))
145 } else {
145 } else {
146 // Reached the bottom of the stack, we’re done
146 // Reached the bottom of the stack, we’re done
147 None
147 None
148 }
148 }
149 })
149 })
150 }
150 }
151
151
152 /// Mutable iterator for the `(entry, copy source)` of each node.
152 /// Mutable iterator for the `(entry, copy source)` of each node.
153 ///
153 ///
154 /// It would not be safe to yield mutable references to nodes themeselves
154 /// It would not be safe to yield mutable references to nodes themeselves
155 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
155 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
156 /// reachable from their ancestor nodes, potentially creating multiple
156 /// reachable from their ancestor nodes, potentially creating multiple
157 /// `&mut` references to a given node.
157 /// `&mut` references to a given node.
158 fn iter_node_data_mut<'a>(
158 fn iter_node_data_mut<'a>(
159 &'a mut self,
159 &'a mut self,
160 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
160 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
161 // Explict stack for pseudo-recursion, see `iter_nodes` above.
161 // Explict stack for pseudo-recursion, see `iter_nodes` above.
162 let mut stack = Vec::new();
162 let mut stack = Vec::new();
163 let mut iter = self.root.iter_mut();
163 let mut iter = self.root.iter_mut();
164 std::iter::from_fn(move || {
164 std::iter::from_fn(move || {
165 while let Some((key, child_node)) = iter.next() {
165 while let Some((key, child_node)) = iter.next() {
166 // Pseudo-recursion
166 // Pseudo-recursion
167 let data =
167 let data =
168 (key, &mut child_node.entry, &mut child_node.copy_source);
168 (key, &mut child_node.entry, &mut child_node.copy_source);
169 let new_iter = child_node.children.iter_mut();
169 let new_iter = child_node.children.iter_mut();
170 let old_iter = std::mem::replace(&mut iter, new_iter);
170 let old_iter = std::mem::replace(&mut iter, new_iter);
171 stack.push((data, old_iter));
171 stack.push((data, old_iter));
172 }
172 }
173 // Found the end of a `children.values_mut()` iterator.
173 // Found the end of a `children.values_mut()` iterator.
174 if let Some((data, next_iter)) = stack.pop() {
174 if let Some((data, next_iter)) = stack.pop() {
175 // "Return" from pseudo-recursion by restoring state from the
175 // "Return" from pseudo-recursion by restoring state from the
176 // explicit stack
176 // explicit stack
177 iter = next_iter;
177 iter = next_iter;
178
178
179 Some(data)
179 Some(data)
180 } else {
180 } else {
181 // Reached the bottom of the stack, we’re done
181 // Reached the bottom of the stack, we’re done
182 None
182 None
183 }
183 }
184 })
184 })
185 }
185 }
186 }
186 }
187
187
188 impl super::dispatch::DirstateMapMethods for DirstateMap {
188 impl super::dispatch::DirstateMapMethods for DirstateMap {
189 fn clear(&mut self) {
189 fn clear(&mut self) {
190 self.set_parents(&DirstateParents {
190 self.set_parents(&DirstateParents {
191 p1: NULL_NODE,
191 p1: NULL_NODE,
192 p2: NULL_NODE,
192 p2: NULL_NODE,
193 });
193 });
194 self.root.clear()
194 self.root.clear()
195 }
195 }
196
196
197 fn add_file(
197 fn add_file(
198 &mut self,
198 &mut self,
199 _filename: &HgPath,
199 _filename: &HgPath,
200 _old_state: EntryState,
200 _old_state: EntryState,
201 _entry: DirstateEntry,
201 _entry: DirstateEntry,
202 ) -> Result<(), DirstateMapError> {
202 ) -> Result<(), DirstateMapError> {
203 todo!()
203 todo!()
204 }
204 }
205
205
206 fn remove_file(
206 fn remove_file(
207 &mut self,
207 &mut self,
208 _filename: &HgPath,
208 _filename: &HgPath,
209 _old_state: EntryState,
209 _old_state: EntryState,
210 _size: i32,
210 _size: i32,
211 ) -> Result<(), DirstateMapError> {
211 ) -> Result<(), DirstateMapError> {
212 todo!()
212 todo!()
213 }
213 }
214
214
215 fn drop_file(
215 fn drop_file(
216 &mut self,
216 &mut self,
217 _filename: &HgPath,
217 _filename: &HgPath,
218 _old_state: EntryState,
218 _old_state: EntryState,
219 ) -> Result<bool, DirstateMapError> {
219 ) -> Result<bool, DirstateMapError> {
220 todo!()
220 todo!()
221 }
221 }
222
222
223 fn clear_ambiguous_times(
223 fn clear_ambiguous_times(
224 &mut self,
224 &mut self,
225 _filenames: Vec<HgPathBuf>,
225 _filenames: Vec<HgPathBuf>,
226 _now: i32,
226 _now: i32,
227 ) {
227 ) {
228 todo!()
228 todo!()
229 }
229 }
230
230
231 fn non_normal_entries_contains(&mut self, _key: &HgPath) -> bool {
231 fn non_normal_entries_contains(&mut self, _key: &HgPath) -> bool {
232 todo!()
232 todo!()
233 }
233 }
234
234
235 fn non_normal_entries_remove(&mut self, _key: &HgPath) -> bool {
235 fn non_normal_entries_remove(&mut self, _key: &HgPath) -> bool {
236 todo!()
236 todo!()
237 }
237 }
238
238
239 fn non_normal_or_other_parent_paths(
239 fn non_normal_or_other_parent_paths(
240 &mut self,
240 &mut self,
241 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
241 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
242 todo!()
242 todo!()
243 }
243 }
244
244
245 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
245 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
246 todo!()
246 todo!()
247 }
247 }
248
248
249 fn iter_non_normal_paths(
249 fn iter_non_normal_paths(
250 &mut self,
250 &mut self,
251 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
251 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
252 todo!()
252 todo!()
253 }
253 }
254
254
255 fn iter_non_normal_paths_panic(
255 fn iter_non_normal_paths_panic(
256 &self,
256 &self,
257 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
257 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
258 todo!()
258 todo!()
259 }
259 }
260
260
261 fn iter_other_parent_paths(
261 fn iter_other_parent_paths(
262 &mut self,
262 &mut self,
263 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
263 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
264 todo!()
264 todo!()
265 }
265 }
266
266
267 fn has_tracked_dir(
267 fn has_tracked_dir(
268 &mut self,
268 &mut self,
269 _directory: &HgPath,
269 _directory: &HgPath,
270 ) -> Result<bool, DirstateMapError> {
270 ) -> Result<bool, DirstateMapError> {
271 todo!()
271 todo!()
272 }
272 }
273
273
274 fn has_dir(
274 fn has_dir(
275 &mut self,
275 &mut self,
276 _directory: &HgPath,
276 _directory: &HgPath,
277 ) -> Result<bool, DirstateMapError> {
277 ) -> Result<bool, DirstateMapError> {
278 todo!()
278 todo!()
279 }
279 }
280
280
281 fn parents(
281 fn parents(
282 &mut self,
282 &mut self,
283 file_contents: &[u8],
283 file_contents: &[u8],
284 ) -> Result<&DirstateParents, DirstateError> {
284 ) -> Result<&DirstateParents, DirstateError> {
285 if self.parents.is_none() {
285 if self.parents.is_none() {
286 let parents = if !file_contents.is_empty() {
286 let parents = if !file_contents.is_empty() {
287 parse_dirstate_parents(file_contents)?.clone()
287 parse_dirstate_parents(file_contents)?.clone()
288 } else {
288 } else {
289 DirstateParents {
289 DirstateParents {
290 p1: NULL_NODE,
290 p1: NULL_NODE,
291 p2: NULL_NODE,
291 p2: NULL_NODE,
292 }
292 }
293 };
293 };
294 self.parents = Some(parents);
294 self.parents = Some(parents);
295 }
295 }
296 Ok(self.parents.as_ref().unwrap())
296 Ok(self.parents.as_ref().unwrap())
297 }
297 }
298
298
299 fn set_parents(&mut self, parents: &DirstateParents) {
299 fn set_parents(&mut self, parents: &DirstateParents) {
300 self.parents = Some(parents.clone());
300 self.parents = Some(parents.clone());
301 self.dirty_parents = true;
301 self.dirty_parents = true;
302 }
302 }
303
303
304 fn read<'a>(
304 fn read<'a>(
305 &mut self,
305 &mut self,
306 file_contents: &'a [u8],
306 file_contents: &'a [u8],
307 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
307 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
308 if file_contents.is_empty() {
308 if file_contents.is_empty() {
309 return Ok(None);
309 return Ok(None);
310 }
310 }
311
311
312 let parents = parse_dirstate_entries(
312 let parents = parse_dirstate_entries(
313 file_contents,
313 file_contents,
314 |path, entry, copy_source| {
314 |path, entry, copy_source| {
315 let node = self.get_or_insert_node(path);
315 let node = self.get_or_insert_node(path);
316 node.entry = Some(*entry);
316 node.entry = Some(*entry);
317 node.copy_source = copy_source.map(HgPath::to_owned);
317 node.copy_source = copy_source.map(HgPath::to_owned);
318 },
318 },
319 )?;
319 )?;
320
320
321 if !self.dirty_parents {
321 if !self.dirty_parents {
322 self.set_parents(parents);
322 self.set_parents(parents);
323 }
323 }
324
324
325 Ok(Some(parents))
325 Ok(Some(parents))
326 }
326 }
327
327
328 fn pack(
328 fn pack(
329 &mut self,
329 &mut self,
330 _parents: DirstateParents,
330 _parents: DirstateParents,
331 _now: Duration,
331 _now: Timestamp,
332 ) -> Result<Vec<u8>, DirstateError> {
332 ) -> Result<Vec<u8>, DirstateError> {
333 let _ = self.iter_node_data_mut();
333 let _ = self.iter_node_data_mut();
334 todo!()
334 todo!()
335 }
335 }
336
336
337 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
337 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
338 todo!()
338 todo!()
339 }
339 }
340
340
341 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
341 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
342 todo!()
342 todo!()
343 }
343 }
344
344
345 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
345 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
346 todo!()
346 todo!()
347 }
347 }
348
348
349 fn status<'a>(
349 fn status<'a>(
350 &'a self,
350 &'a self,
351 _matcher: &'a (dyn Matcher + Sync),
351 _matcher: &'a (dyn Matcher + Sync),
352 _root_dir: PathBuf,
352 _root_dir: PathBuf,
353 _ignore_files: Vec<PathBuf>,
353 _ignore_files: Vec<PathBuf>,
354 _options: StatusOptions,
354 _options: StatusOptions,
355 ) -> Result<
355 ) -> Result<
356 (
356 (
357 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
357 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
358 Vec<PatternFileWarning>,
358 Vec<PatternFileWarning>,
359 ),
359 ),
360 StatusError,
360 StatusError,
361 > {
361 > {
362 todo!()
362 todo!()
363 }
363 }
364
364
365 fn copy_map_len(&self) -> usize {
365 fn copy_map_len(&self) -> usize {
366 todo!()
366 todo!()
367 }
367 }
368
368
369 fn copy_map_iter(&self) -> CopyMapIter<'_> {
369 fn copy_map_iter(&self) -> CopyMapIter<'_> {
370 Box::new(self.iter_nodes().filter_map(|(path, node)| {
370 Box::new(self.iter_nodes().filter_map(|(path, node)| {
371 node.copy_source
371 node.copy_source
372 .as_ref()
372 .as_ref()
373 .map(|copy_source| (path.full_path(), copy_source))
373 .map(|copy_source| (path.full_path(), copy_source))
374 }))
374 }))
375 }
375 }
376
376
377 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
377 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
378 if let Some(node) = self.get_node(key) {
378 if let Some(node) = self.get_node(key) {
379 node.copy_source.is_some()
379 node.copy_source.is_some()
380 } else {
380 } else {
381 false
381 false
382 }
382 }
383 }
383 }
384
384
385 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
385 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
386 self.get_node(key)?.copy_source.as_ref()
386 self.get_node(key)?.copy_source.as_ref()
387 }
387 }
388
388
389 fn copy_map_remove(&mut self, _key: &HgPath) -> Option<HgPathBuf> {
389 fn copy_map_remove(&mut self, _key: &HgPath) -> Option<HgPathBuf> {
390 todo!()
390 todo!()
391 }
391 }
392
392
393 fn copy_map_insert(
393 fn copy_map_insert(
394 &mut self,
394 &mut self,
395 _key: HgPathBuf,
395 _key: HgPathBuf,
396 _value: HgPathBuf,
396 _value: HgPathBuf,
397 ) -> Option<HgPathBuf> {
397 ) -> Option<HgPathBuf> {
398 todo!()
398 todo!()
399 }
399 }
400
400
401 fn len(&self) -> usize {
401 fn len(&self) -> usize {
402 todo!()
402 todo!()
403 }
403 }
404
404
405 fn contains_key(&self, key: &HgPath) -> bool {
405 fn contains_key(&self, key: &HgPath) -> bool {
406 self.get(key).is_some()
406 self.get(key).is_some()
407 }
407 }
408
408
409 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
409 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
410 self.get_node(key)?.entry.as_ref()
410 self.get_node(key)?.entry.as_ref()
411 }
411 }
412
412
413 fn iter(&self) -> StateMapIter<'_> {
413 fn iter(&self) -> StateMapIter<'_> {
414 Box::new(self.iter_nodes().filter_map(|(path, node)| {
414 Box::new(self.iter_nodes().filter_map(|(path, node)| {
415 node.entry.as_ref().map(|entry| (path.full_path(), entry))
415 node.entry.as_ref().map(|entry| (path.full_path(), entry))
416 }))
416 }))
417 }
417 }
418 }
418 }
@@ -1,333 +1,333
1 use std::path::PathBuf;
1 use std::path::PathBuf;
2 use std::time::Duration;
3
2
3 use crate::dirstate::parsers::Timestamp;
4 use crate::matchers::Matcher;
4 use crate::matchers::Matcher;
5 use crate::utils::hg_path::{HgPath, HgPathBuf};
5 use crate::utils::hg_path::{HgPath, HgPathBuf};
6 use crate::CopyMapIter;
6 use crate::CopyMapIter;
7 use crate::DirstateEntry;
7 use crate::DirstateEntry;
8 use crate::DirstateError;
8 use crate::DirstateError;
9 use crate::DirstateMap;
9 use crate::DirstateMap;
10 use crate::DirstateMapError;
10 use crate::DirstateMapError;
11 use crate::DirstateParents;
11 use crate::DirstateParents;
12 use crate::DirstateStatus;
12 use crate::DirstateStatus;
13 use crate::EntryState;
13 use crate::EntryState;
14 use crate::FastHashMap;
14 use crate::FastHashMap;
15 use crate::HgPathCow;
15 use crate::HgPathCow;
16 use crate::PatternFileWarning;
16 use crate::PatternFileWarning;
17 use crate::StateMapIter;
17 use crate::StateMapIter;
18 use crate::StatusError;
18 use crate::StatusError;
19 use crate::StatusOptions;
19 use crate::StatusOptions;
20
20
21 pub trait DirstateMapMethods {
21 pub trait DirstateMapMethods {
22 fn clear(&mut self);
22 fn clear(&mut self);
23
23
24 fn add_file(
24 fn add_file(
25 &mut self,
25 &mut self,
26 filename: &HgPath,
26 filename: &HgPath,
27 old_state: EntryState,
27 old_state: EntryState,
28 entry: DirstateEntry,
28 entry: DirstateEntry,
29 ) -> Result<(), DirstateMapError>;
29 ) -> Result<(), DirstateMapError>;
30
30
31 fn remove_file(
31 fn remove_file(
32 &mut self,
32 &mut self,
33 filename: &HgPath,
33 filename: &HgPath,
34 old_state: EntryState,
34 old_state: EntryState,
35 size: i32,
35 size: i32,
36 ) -> Result<(), DirstateMapError>;
36 ) -> Result<(), DirstateMapError>;
37
37
38 fn drop_file(
38 fn drop_file(
39 &mut self,
39 &mut self,
40 filename: &HgPath,
40 filename: &HgPath,
41 old_state: EntryState,
41 old_state: EntryState,
42 ) -> Result<bool, DirstateMapError>;
42 ) -> Result<bool, DirstateMapError>;
43
43
44 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32);
44 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32);
45
45
46 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool;
46 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool;
47
47
48 fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool;
48 fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool;
49
49
50 fn non_normal_or_other_parent_paths(
50 fn non_normal_or_other_parent_paths(
51 &mut self,
51 &mut self,
52 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_>;
52 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_>;
53
53
54 fn set_non_normal_other_parent_entries(&mut self, force: bool);
54 fn set_non_normal_other_parent_entries(&mut self, force: bool);
55
55
56 fn iter_non_normal_paths(
56 fn iter_non_normal_paths(
57 &mut self,
57 &mut self,
58 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
58 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
59
59
60 fn iter_non_normal_paths_panic(
60 fn iter_non_normal_paths_panic(
61 &self,
61 &self,
62 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
62 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
63
63
64 fn iter_other_parent_paths(
64 fn iter_other_parent_paths(
65 &mut self,
65 &mut self,
66 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
66 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
67
67
68 fn has_tracked_dir(
68 fn has_tracked_dir(
69 &mut self,
69 &mut self,
70 directory: &HgPath,
70 directory: &HgPath,
71 ) -> Result<bool, DirstateMapError>;
71 ) -> Result<bool, DirstateMapError>;
72
72
73 fn has_dir(
73 fn has_dir(
74 &mut self,
74 &mut self,
75 directory: &HgPath,
75 directory: &HgPath,
76 ) -> Result<bool, DirstateMapError>;
76 ) -> Result<bool, DirstateMapError>;
77
77
78 fn parents(
78 fn parents(
79 &mut self,
79 &mut self,
80 file_contents: &[u8],
80 file_contents: &[u8],
81 ) -> Result<&DirstateParents, DirstateError>;
81 ) -> Result<&DirstateParents, DirstateError>;
82
82
83 fn set_parents(&mut self, parents: &DirstateParents);
83 fn set_parents(&mut self, parents: &DirstateParents);
84
84
85 fn read<'a>(
85 fn read<'a>(
86 &mut self,
86 &mut self,
87 file_contents: &'a [u8],
87 file_contents: &'a [u8],
88 ) -> Result<Option<&'a DirstateParents>, DirstateError>;
88 ) -> Result<Option<&'a DirstateParents>, DirstateError>;
89
89
90 fn pack(
90 fn pack(
91 &mut self,
91 &mut self,
92 parents: DirstateParents,
92 parents: DirstateParents,
93 now: Duration,
93 now: Timestamp,
94 ) -> Result<Vec<u8>, DirstateError>;
94 ) -> Result<Vec<u8>, DirstateError>;
95
95
96 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf>;
96 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf>;
97
97
98 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError>;
98 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError>;
99
99
100 fn set_dirs(&mut self) -> Result<(), DirstateMapError>;
100 fn set_dirs(&mut self) -> Result<(), DirstateMapError>;
101
101
102 fn status<'a>(
102 fn status<'a>(
103 &'a self,
103 &'a self,
104 matcher: &'a (dyn Matcher + Sync),
104 matcher: &'a (dyn Matcher + Sync),
105 root_dir: PathBuf,
105 root_dir: PathBuf,
106 ignore_files: Vec<PathBuf>,
106 ignore_files: Vec<PathBuf>,
107 options: StatusOptions,
107 options: StatusOptions,
108 ) -> Result<
108 ) -> Result<
109 (
109 (
110 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
110 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
111 Vec<PatternFileWarning>,
111 Vec<PatternFileWarning>,
112 ),
112 ),
113 StatusError,
113 StatusError,
114 >;
114 >;
115
115
116 fn copy_map_len(&self) -> usize;
116 fn copy_map_len(&self) -> usize;
117
117
118 fn copy_map_iter(&self) -> CopyMapIter<'_>;
118 fn copy_map_iter(&self) -> CopyMapIter<'_>;
119
119
120 fn copy_map_contains_key(&self, key: &HgPath) -> bool;
120 fn copy_map_contains_key(&self, key: &HgPath) -> bool;
121
121
122 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf>;
122 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf>;
123
123
124 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf>;
124 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf>;
125
125
126 fn copy_map_insert(
126 fn copy_map_insert(
127 &mut self,
127 &mut self,
128 key: HgPathBuf,
128 key: HgPathBuf,
129 value: HgPathBuf,
129 value: HgPathBuf,
130 ) -> Option<HgPathBuf>;
130 ) -> Option<HgPathBuf>;
131
131
132 fn len(&self) -> usize;
132 fn len(&self) -> usize;
133
133
134 fn contains_key(&self, key: &HgPath) -> bool;
134 fn contains_key(&self, key: &HgPath) -> bool;
135
135
136 fn get(&self, key: &HgPath) -> Option<&DirstateEntry>;
136 fn get(&self, key: &HgPath) -> Option<&DirstateEntry>;
137
137
138 fn iter(&self) -> StateMapIter<'_>;
138 fn iter(&self) -> StateMapIter<'_>;
139 }
139 }
140
140
141 impl DirstateMapMethods for DirstateMap {
141 impl DirstateMapMethods for DirstateMap {
142 fn clear(&mut self) {
142 fn clear(&mut self) {
143 self.clear()
143 self.clear()
144 }
144 }
145
145
146 fn add_file(
146 fn add_file(
147 &mut self,
147 &mut self,
148 filename: &HgPath,
148 filename: &HgPath,
149 old_state: EntryState,
149 old_state: EntryState,
150 entry: DirstateEntry,
150 entry: DirstateEntry,
151 ) -> Result<(), DirstateMapError> {
151 ) -> Result<(), DirstateMapError> {
152 self.add_file(filename, old_state, entry)
152 self.add_file(filename, old_state, entry)
153 }
153 }
154
154
155 fn remove_file(
155 fn remove_file(
156 &mut self,
156 &mut self,
157 filename: &HgPath,
157 filename: &HgPath,
158 old_state: EntryState,
158 old_state: EntryState,
159 size: i32,
159 size: i32,
160 ) -> Result<(), DirstateMapError> {
160 ) -> Result<(), DirstateMapError> {
161 self.remove_file(filename, old_state, size)
161 self.remove_file(filename, old_state, size)
162 }
162 }
163
163
164 fn drop_file(
164 fn drop_file(
165 &mut self,
165 &mut self,
166 filename: &HgPath,
166 filename: &HgPath,
167 old_state: EntryState,
167 old_state: EntryState,
168 ) -> Result<bool, DirstateMapError> {
168 ) -> Result<bool, DirstateMapError> {
169 self.drop_file(filename, old_state)
169 self.drop_file(filename, old_state)
170 }
170 }
171
171
172 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
172 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
173 self.clear_ambiguous_times(filenames, now)
173 self.clear_ambiguous_times(filenames, now)
174 }
174 }
175
175
176 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
176 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
177 let (non_normal, _other_parent) =
177 let (non_normal, _other_parent) =
178 self.get_non_normal_other_parent_entries();
178 self.get_non_normal_other_parent_entries();
179 non_normal.contains(key)
179 non_normal.contains(key)
180 }
180 }
181
181
182 fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool {
182 fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool {
183 self.non_normal_entries_remove(key)
183 self.non_normal_entries_remove(key)
184 }
184 }
185
185
186 fn non_normal_or_other_parent_paths(
186 fn non_normal_or_other_parent_paths(
187 &mut self,
187 &mut self,
188 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
188 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
189 let (non_normal, other_parent) =
189 let (non_normal, other_parent) =
190 self.get_non_normal_other_parent_entries();
190 self.get_non_normal_other_parent_entries();
191 Box::new(non_normal.union(other_parent))
191 Box::new(non_normal.union(other_parent))
192 }
192 }
193
193
194 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
194 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
195 self.set_non_normal_other_parent_entries(force)
195 self.set_non_normal_other_parent_entries(force)
196 }
196 }
197
197
198 fn iter_non_normal_paths(
198 fn iter_non_normal_paths(
199 &mut self,
199 &mut self,
200 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
200 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
201 let (non_normal, _other_parent) =
201 let (non_normal, _other_parent) =
202 self.get_non_normal_other_parent_entries();
202 self.get_non_normal_other_parent_entries();
203 Box::new(non_normal.iter())
203 Box::new(non_normal.iter())
204 }
204 }
205
205
206 fn iter_non_normal_paths_panic(
206 fn iter_non_normal_paths_panic(
207 &self,
207 &self,
208 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
208 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
209 let (non_normal, _other_parent) =
209 let (non_normal, _other_parent) =
210 self.get_non_normal_other_parent_entries_panic();
210 self.get_non_normal_other_parent_entries_panic();
211 Box::new(non_normal.iter())
211 Box::new(non_normal.iter())
212 }
212 }
213
213
214 fn iter_other_parent_paths(
214 fn iter_other_parent_paths(
215 &mut self,
215 &mut self,
216 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
216 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
217 let (_non_normal, other_parent) =
217 let (_non_normal, other_parent) =
218 self.get_non_normal_other_parent_entries();
218 self.get_non_normal_other_parent_entries();
219 Box::new(other_parent.iter())
219 Box::new(other_parent.iter())
220 }
220 }
221
221
222 fn has_tracked_dir(
222 fn has_tracked_dir(
223 &mut self,
223 &mut self,
224 directory: &HgPath,
224 directory: &HgPath,
225 ) -> Result<bool, DirstateMapError> {
225 ) -> Result<bool, DirstateMapError> {
226 self.has_tracked_dir(directory)
226 self.has_tracked_dir(directory)
227 }
227 }
228
228
229 fn has_dir(
229 fn has_dir(
230 &mut self,
230 &mut self,
231 directory: &HgPath,
231 directory: &HgPath,
232 ) -> Result<bool, DirstateMapError> {
232 ) -> Result<bool, DirstateMapError> {
233 self.has_dir(directory)
233 self.has_dir(directory)
234 }
234 }
235
235
236 fn parents(
236 fn parents(
237 &mut self,
237 &mut self,
238 file_contents: &[u8],
238 file_contents: &[u8],
239 ) -> Result<&DirstateParents, DirstateError> {
239 ) -> Result<&DirstateParents, DirstateError> {
240 self.parents(file_contents)
240 self.parents(file_contents)
241 }
241 }
242
242
243 fn set_parents(&mut self, parents: &DirstateParents) {
243 fn set_parents(&mut self, parents: &DirstateParents) {
244 self.set_parents(parents)
244 self.set_parents(parents)
245 }
245 }
246
246
247 fn read<'a>(
247 fn read<'a>(
248 &mut self,
248 &mut self,
249 file_contents: &'a [u8],
249 file_contents: &'a [u8],
250 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
250 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
251 self.read(file_contents)
251 self.read(file_contents)
252 }
252 }
253
253
254 fn pack(
254 fn pack(
255 &mut self,
255 &mut self,
256 parents: DirstateParents,
256 parents: DirstateParents,
257 now: Duration,
257 now: Timestamp,
258 ) -> Result<Vec<u8>, DirstateError> {
258 ) -> Result<Vec<u8>, DirstateError> {
259 self.pack(parents, now)
259 self.pack(parents, now)
260 }
260 }
261
261
262 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
262 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
263 self.build_file_fold_map()
263 self.build_file_fold_map()
264 }
264 }
265
265
266 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
266 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
267 self.set_all_dirs()
267 self.set_all_dirs()
268 }
268 }
269
269
270 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
270 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
271 self.set_dirs()
271 self.set_dirs()
272 }
272 }
273
273
274 fn status<'a>(
274 fn status<'a>(
275 &'a self,
275 &'a self,
276 matcher: &'a (dyn Matcher + Sync),
276 matcher: &'a (dyn Matcher + Sync),
277 root_dir: PathBuf,
277 root_dir: PathBuf,
278 ignore_files: Vec<PathBuf>,
278 ignore_files: Vec<PathBuf>,
279 options: StatusOptions,
279 options: StatusOptions,
280 ) -> Result<
280 ) -> Result<
281 (
281 (
282 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
282 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
283 Vec<PatternFileWarning>,
283 Vec<PatternFileWarning>,
284 ),
284 ),
285 StatusError,
285 StatusError,
286 > {
286 > {
287 crate::status(self, matcher, root_dir, ignore_files, options)
287 crate::status(self, matcher, root_dir, ignore_files, options)
288 }
288 }
289
289
290 fn copy_map_len(&self) -> usize {
290 fn copy_map_len(&self) -> usize {
291 self.copy_map.len()
291 self.copy_map.len()
292 }
292 }
293
293
294 fn copy_map_iter(&self) -> CopyMapIter<'_> {
294 fn copy_map_iter(&self) -> CopyMapIter<'_> {
295 Box::new(self.copy_map.iter())
295 Box::new(self.copy_map.iter())
296 }
296 }
297
297
298 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
298 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
299 self.copy_map.contains_key(key)
299 self.copy_map.contains_key(key)
300 }
300 }
301
301
302 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
302 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
303 self.copy_map.get(key)
303 self.copy_map.get(key)
304 }
304 }
305
305
306 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
306 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
307 self.copy_map.remove(key)
307 self.copy_map.remove(key)
308 }
308 }
309
309
310 fn copy_map_insert(
310 fn copy_map_insert(
311 &mut self,
311 &mut self,
312 key: HgPathBuf,
312 key: HgPathBuf,
313 value: HgPathBuf,
313 value: HgPathBuf,
314 ) -> Option<HgPathBuf> {
314 ) -> Option<HgPathBuf> {
315 self.copy_map.insert(key, value)
315 self.copy_map.insert(key, value)
316 }
316 }
317
317
318 fn len(&self) -> usize {
318 fn len(&self) -> usize {
319 (&**self).len()
319 (&**self).len()
320 }
320 }
321
321
322 fn contains_key(&self, key: &HgPath) -> bool {
322 fn contains_key(&self, key: &HgPath) -> bool {
323 (&**self).contains_key(key)
323 (&**self).contains_key(key)
324 }
324 }
325
325
326 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
326 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
327 (&**self).get(key)
327 (&**self).get(key)
328 }
328 }
329
329
330 fn iter(&self) -> StateMapIter<'_> {
330 fn iter(&self) -> StateMapIter<'_> {
331 Box::new((&**self).iter())
331 Box::new((&**self).iter())
332 }
332 }
333 }
333 }
@@ -1,123 +1,123
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 // and Mercurial contributors
2 // and Mercurial contributors
3 //
3 //
4 // This software may be used and distributed according to the terms of the
4 // This software may be used and distributed according to the terms of the
5 // GNU General Public License version 2 or any later version.
5 // GNU General Public License version 2 or any later version.
6
6
7 mod ancestors;
7 mod ancestors;
8 pub mod dagops;
8 pub mod dagops;
9 pub mod errors;
9 pub mod errors;
10 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
10 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
11 mod dirstate;
11 pub mod dirstate;
12 pub mod dirstate_tree;
12 pub mod dirstate_tree;
13 pub mod discovery;
13 pub mod discovery;
14 pub mod requirements;
14 pub mod requirements;
15 pub mod testing; // unconditionally built, for use from integration tests
15 pub mod testing; // unconditionally built, for use from integration tests
16 pub use dirstate::{
16 pub use dirstate::{
17 dirs_multiset::{DirsMultiset, DirsMultisetIter},
17 dirs_multiset::{DirsMultiset, DirsMultisetIter},
18 dirstate_map::DirstateMap,
18 dirstate_map::DirstateMap,
19 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
19 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
20 status::{
20 status::{
21 status, BadMatch, BadType, DirstateStatus, HgPathCow, StatusError,
21 status, BadMatch, BadType, DirstateStatus, HgPathCow, StatusError,
22 StatusOptions,
22 StatusOptions,
23 },
23 },
24 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
24 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
25 StateMap, StateMapIter,
25 StateMap, StateMapIter,
26 };
26 };
27 pub mod copy_tracing;
27 pub mod copy_tracing;
28 mod filepatterns;
28 mod filepatterns;
29 pub mod matchers;
29 pub mod matchers;
30 pub mod repo;
30 pub mod repo;
31 pub mod revlog;
31 pub mod revlog;
32 pub use revlog::*;
32 pub use revlog::*;
33 pub mod config;
33 pub mod config;
34 pub mod logging;
34 pub mod logging;
35 pub mod operations;
35 pub mod operations;
36 pub mod revset;
36 pub mod revset;
37 pub mod utils;
37 pub mod utils;
38
38
39 use crate::utils::hg_path::{HgPathBuf, HgPathError};
39 use crate::utils::hg_path::{HgPathBuf, HgPathError};
40 pub use filepatterns::{
40 pub use filepatterns::{
41 parse_pattern_syntax, read_pattern_file, IgnorePattern,
41 parse_pattern_syntax, read_pattern_file, IgnorePattern,
42 PatternFileWarning, PatternSyntax,
42 PatternFileWarning, PatternSyntax,
43 };
43 };
44 use std::collections::HashMap;
44 use std::collections::HashMap;
45 use std::fmt;
45 use std::fmt;
46 use twox_hash::RandomXxHashBuilder64;
46 use twox_hash::RandomXxHashBuilder64;
47
47
48 /// This is a contract between the `micro-timer` crate and us, to expose
48 /// This is a contract between the `micro-timer` crate and us, to expose
49 /// the `log` crate as `crate::log`.
49 /// the `log` crate as `crate::log`.
50 use log;
50 use log;
51
51
52 pub type LineNumber = usize;
52 pub type LineNumber = usize;
53
53
54 /// Rust's default hasher is too slow because it tries to prevent collision
54 /// Rust's default hasher is too slow because it tries to prevent collision
55 /// attacks. We are not concerned about those: if an ill-minded person has
55 /// attacks. We are not concerned about those: if an ill-minded person has
56 /// write access to your repository, you have other issues.
56 /// write access to your repository, you have other issues.
57 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
57 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
58
58
59 #[derive(Debug, PartialEq)]
59 #[derive(Debug, PartialEq)]
60 pub enum DirstateMapError {
60 pub enum DirstateMapError {
61 PathNotFound(HgPathBuf),
61 PathNotFound(HgPathBuf),
62 EmptyPath,
62 EmptyPath,
63 InvalidPath(HgPathError),
63 InvalidPath(HgPathError),
64 }
64 }
65
65
66 impl fmt::Display for DirstateMapError {
66 impl fmt::Display for DirstateMapError {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 match self {
68 match self {
69 DirstateMapError::PathNotFound(_) => {
69 DirstateMapError::PathNotFound(_) => {
70 f.write_str("expected a value, found none")
70 f.write_str("expected a value, found none")
71 }
71 }
72 DirstateMapError::EmptyPath => {
72 DirstateMapError::EmptyPath => {
73 f.write_str("Overflow in dirstate.")
73 f.write_str("Overflow in dirstate.")
74 }
74 }
75 DirstateMapError::InvalidPath(path_error) => path_error.fmt(f),
75 DirstateMapError::InvalidPath(path_error) => path_error.fmt(f),
76 }
76 }
77 }
77 }
78 }
78 }
79
79
80 #[derive(Debug, derive_more::From)]
80 #[derive(Debug, derive_more::From)]
81 pub enum DirstateError {
81 pub enum DirstateError {
82 Map(DirstateMapError),
82 Map(DirstateMapError),
83 Common(errors::HgError),
83 Common(errors::HgError),
84 }
84 }
85
85
86 #[derive(Debug, derive_more::From)]
86 #[derive(Debug, derive_more::From)]
87 pub enum PatternError {
87 pub enum PatternError {
88 #[from]
88 #[from]
89 Path(HgPathError),
89 Path(HgPathError),
90 UnsupportedSyntax(String),
90 UnsupportedSyntax(String),
91 UnsupportedSyntaxInFile(String, String, usize),
91 UnsupportedSyntaxInFile(String, String, usize),
92 TooLong(usize),
92 TooLong(usize),
93 #[from]
93 #[from]
94 IO(std::io::Error),
94 IO(std::io::Error),
95 /// Needed a pattern that can be turned into a regex but got one that
95 /// Needed a pattern that can be turned into a regex but got one that
96 /// can't. This should only happen through programmer error.
96 /// can't. This should only happen through programmer error.
97 NonRegexPattern(IgnorePattern),
97 NonRegexPattern(IgnorePattern),
98 }
98 }
99
99
100 impl fmt::Display for PatternError {
100 impl fmt::Display for PatternError {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 match self {
102 match self {
103 PatternError::UnsupportedSyntax(syntax) => {
103 PatternError::UnsupportedSyntax(syntax) => {
104 write!(f, "Unsupported syntax {}", syntax)
104 write!(f, "Unsupported syntax {}", syntax)
105 }
105 }
106 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
106 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
107 write!(
107 write!(
108 f,
108 f,
109 "{}:{}: unsupported syntax {}",
109 "{}:{}: unsupported syntax {}",
110 file_path, line, syntax
110 file_path, line, syntax
111 )
111 )
112 }
112 }
113 PatternError::TooLong(size) => {
113 PatternError::TooLong(size) => {
114 write!(f, "matcher pattern is too long ({} bytes)", size)
114 write!(f, "matcher pattern is too long ({} bytes)", size)
115 }
115 }
116 PatternError::IO(error) => error.fmt(f),
116 PatternError::IO(error) => error.fmt(f),
117 PatternError::Path(error) => error.fmt(f),
117 PatternError::Path(error) => error.fmt(f),
118 PatternError::NonRegexPattern(pattern) => {
118 PatternError::NonRegexPattern(pattern) => {
119 write!(f, "'{:?}' cannot be turned into a regex", pattern)
119 write!(f, "'{:?}' cannot be turned into a regex", pattern)
120 }
120 }
121 }
121 }
122 }
122 }
123 }
123 }
@@ -1,571 +1,571
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
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 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::{Ref, RefCell};
11 use std::cell::{Ref, RefCell};
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13 use std::time::Duration;
14
13
15 use cpython::{
14 use cpython::{
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
15 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
17 PyObject, PyResult, PySet, PyString, PyTuple, Python, PythonObject,
16 PyObject, PyResult, PySet, PyString, PyTuple, Python, PythonObject,
18 ToPyObject, UnsafePyLeaked,
17 ToPyObject, UnsafePyLeaked,
19 };
18 };
20
19
21 use crate::{
20 use crate::{
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::non_normal_entries::{
22 dirstate::non_normal_entries::{
24 NonNormalEntries, NonNormalEntriesIterator,
23 NonNormalEntries, NonNormalEntriesIterator,
25 },
24 },
26 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
25 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
27 parsers::dirstate_parents_to_pytuple,
26 parsers::dirstate_parents_to_pytuple,
28 };
27 };
29 use hg::{
28 use hg::{
29 dirstate::parsers::Timestamp,
30 dirstate_tree::dispatch::DirstateMapMethods,
30 dirstate_tree::dispatch::DirstateMapMethods,
31 errors::HgError,
31 errors::HgError,
32 revlog::Node,
32 revlog::Node,
33 utils::hg_path::{HgPath, HgPathBuf},
33 utils::hg_path::{HgPath, HgPathBuf},
34 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
34 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
35 DirstateMapError, DirstateParents, EntryState, StateMapIter,
35 DirstateMapError, DirstateParents, EntryState, StateMapIter,
36 };
36 };
37
37
38 // TODO
38 // TODO
39 // This object needs to share references to multiple members of its Rust
39 // This object needs to share references to multiple members of its Rust
40 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
40 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
41 // Right now `CopyMap` is done, but it needs to have an explicit reference
41 // Right now `CopyMap` is done, but it needs to have an explicit reference
42 // to `RustDirstateMap` which itself needs to have an encapsulation for
42 // to `RustDirstateMap` which itself needs to have an encapsulation for
43 // every method in `CopyMap` (copymapcopy, etc.).
43 // every method in `CopyMap` (copymapcopy, etc.).
44 // This is ugly and hard to maintain.
44 // This is ugly and hard to maintain.
45 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
45 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
46 // `py_class!` is already implemented and does not mention
46 // `py_class!` is already implemented and does not mention
47 // `RustDirstateMap`, rightfully so.
47 // `RustDirstateMap`, rightfully so.
48 // All attributes also have to have a separate refcount data attribute for
48 // All attributes also have to have a separate refcount data attribute for
49 // leaks, with all methods that go along for reference sharing.
49 // leaks, with all methods that go along for reference sharing.
50 py_class!(pub class DirstateMap |py| {
50 py_class!(pub class DirstateMap |py| {
51 @shared data inner: Box<dyn DirstateMapMethods + Send>;
51 @shared data inner: Box<dyn DirstateMapMethods + Send>;
52
52
53 def __new__(_cls, use_dirstate_tree: bool) -> PyResult<Self> {
53 def __new__(_cls, use_dirstate_tree: bool) -> PyResult<Self> {
54 let inner = if use_dirstate_tree {
54 let inner = if use_dirstate_tree {
55 Box::new(hg::dirstate_tree::dirstate_map::DirstateMap::new()) as _
55 Box::new(hg::dirstate_tree::dirstate_map::DirstateMap::new()) as _
56 } else {
56 } else {
57 Box::new(RustDirstateMap::default()) as _
57 Box::new(RustDirstateMap::default()) as _
58 };
58 };
59 Self::create_instance(py, inner)
59 Self::create_instance(py, inner)
60 }
60 }
61
61
62 def clear(&self) -> PyResult<PyObject> {
62 def clear(&self) -> PyResult<PyObject> {
63 self.inner(py).borrow_mut().clear();
63 self.inner(py).borrow_mut().clear();
64 Ok(py.None())
64 Ok(py.None())
65 }
65 }
66
66
67 def get(
67 def get(
68 &self,
68 &self,
69 key: PyObject,
69 key: PyObject,
70 default: Option<PyObject> = None
70 default: Option<PyObject> = None
71 ) -> PyResult<Option<PyObject>> {
71 ) -> PyResult<Option<PyObject>> {
72 let key = key.extract::<PyBytes>(py)?;
72 let key = key.extract::<PyBytes>(py)?;
73 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
73 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
74 Some(entry) => {
74 Some(entry) => {
75 Ok(Some(make_dirstate_tuple(py, entry)?))
75 Ok(Some(make_dirstate_tuple(py, entry)?))
76 },
76 },
77 None => Ok(default)
77 None => Ok(default)
78 }
78 }
79 }
79 }
80
80
81 def addfile(
81 def addfile(
82 &self,
82 &self,
83 f: PyObject,
83 f: PyObject,
84 oldstate: PyObject,
84 oldstate: PyObject,
85 state: PyObject,
85 state: PyObject,
86 mode: PyObject,
86 mode: PyObject,
87 size: PyObject,
87 size: PyObject,
88 mtime: PyObject
88 mtime: PyObject
89 ) -> PyResult<PyObject> {
89 ) -> PyResult<PyObject> {
90 self.inner(py).borrow_mut().add_file(
90 self.inner(py).borrow_mut().add_file(
91 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
91 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
92 oldstate.extract::<PyBytes>(py)?.data(py)[0]
92 oldstate.extract::<PyBytes>(py)?.data(py)[0]
93 .try_into()
93 .try_into()
94 .map_err(|e: HgError| {
94 .map_err(|e: HgError| {
95 PyErr::new::<exc::ValueError, _>(py, e.to_string())
95 PyErr::new::<exc::ValueError, _>(py, e.to_string())
96 })?,
96 })?,
97 DirstateEntry {
97 DirstateEntry {
98 state: state.extract::<PyBytes>(py)?.data(py)[0]
98 state: state.extract::<PyBytes>(py)?.data(py)[0]
99 .try_into()
99 .try_into()
100 .map_err(|e: HgError| {
100 .map_err(|e: HgError| {
101 PyErr::new::<exc::ValueError, _>(py, e.to_string())
101 PyErr::new::<exc::ValueError, _>(py, e.to_string())
102 })?,
102 })?,
103 mode: mode.extract(py)?,
103 mode: mode.extract(py)?,
104 size: size.extract(py)?,
104 size: size.extract(py)?,
105 mtime: mtime.extract(py)?,
105 mtime: mtime.extract(py)?,
106 },
106 },
107 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
107 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
108 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
108 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
109 })
109 })
110 }
110 }
111
111
112 def removefile(
112 def removefile(
113 &self,
113 &self,
114 f: PyObject,
114 f: PyObject,
115 oldstate: PyObject,
115 oldstate: PyObject,
116 size: PyObject
116 size: PyObject
117 ) -> PyResult<PyObject> {
117 ) -> PyResult<PyObject> {
118 self.inner(py).borrow_mut()
118 self.inner(py).borrow_mut()
119 .remove_file(
119 .remove_file(
120 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
120 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
121 oldstate.extract::<PyBytes>(py)?.data(py)[0]
121 oldstate.extract::<PyBytes>(py)?.data(py)[0]
122 .try_into()
122 .try_into()
123 .map_err(|e: HgError| {
123 .map_err(|e: HgError| {
124 PyErr::new::<exc::ValueError, _>(py, e.to_string())
124 PyErr::new::<exc::ValueError, _>(py, e.to_string())
125 })?,
125 })?,
126 size.extract(py)?,
126 size.extract(py)?,
127 )
127 )
128 .or_else(|_| {
128 .or_else(|_| {
129 Err(PyErr::new::<exc::OSError, _>(
129 Err(PyErr::new::<exc::OSError, _>(
130 py,
130 py,
131 "Dirstate error".to_string(),
131 "Dirstate error".to_string(),
132 ))
132 ))
133 })?;
133 })?;
134 Ok(py.None())
134 Ok(py.None())
135 }
135 }
136
136
137 def dropfile(
137 def dropfile(
138 &self,
138 &self,
139 f: PyObject,
139 f: PyObject,
140 oldstate: PyObject
140 oldstate: PyObject
141 ) -> PyResult<PyBool> {
141 ) -> PyResult<PyBool> {
142 self.inner(py).borrow_mut()
142 self.inner(py).borrow_mut()
143 .drop_file(
143 .drop_file(
144 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
144 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
145 oldstate.extract::<PyBytes>(py)?.data(py)[0]
145 oldstate.extract::<PyBytes>(py)?.data(py)[0]
146 .try_into()
146 .try_into()
147 .map_err(|e: HgError| {
147 .map_err(|e: HgError| {
148 PyErr::new::<exc::ValueError, _>(py, e.to_string())
148 PyErr::new::<exc::ValueError, _>(py, e.to_string())
149 })?,
149 })?,
150 )
150 )
151 .and_then(|b| Ok(b.to_py_object(py)))
151 .and_then(|b| Ok(b.to_py_object(py)))
152 .or_else(|e| {
152 .or_else(|e| {
153 Err(PyErr::new::<exc::OSError, _>(
153 Err(PyErr::new::<exc::OSError, _>(
154 py,
154 py,
155 format!("Dirstate error: {}", e.to_string()),
155 format!("Dirstate error: {}", e.to_string()),
156 ))
156 ))
157 })
157 })
158 }
158 }
159
159
160 def clearambiguoustimes(
160 def clearambiguoustimes(
161 &self,
161 &self,
162 files: PyObject,
162 files: PyObject,
163 now: PyObject
163 now: PyObject
164 ) -> PyResult<PyObject> {
164 ) -> PyResult<PyObject> {
165 let files: PyResult<Vec<HgPathBuf>> = files
165 let files: PyResult<Vec<HgPathBuf>> = files
166 .iter(py)?
166 .iter(py)?
167 .map(|filename| {
167 .map(|filename| {
168 Ok(HgPathBuf::from_bytes(
168 Ok(HgPathBuf::from_bytes(
169 filename?.extract::<PyBytes>(py)?.data(py),
169 filename?.extract::<PyBytes>(py)?.data(py),
170 ))
170 ))
171 })
171 })
172 .collect();
172 .collect();
173 self.inner(py).borrow_mut()
173 self.inner(py).borrow_mut()
174 .clear_ambiguous_times(files?, now.extract(py)?);
174 .clear_ambiguous_times(files?, now.extract(py)?);
175 Ok(py.None())
175 Ok(py.None())
176 }
176 }
177
177
178 def other_parent_entries(&self) -> PyResult<PyObject> {
178 def other_parent_entries(&self) -> PyResult<PyObject> {
179 let mut inner_shared = self.inner(py).borrow_mut();
179 let mut inner_shared = self.inner(py).borrow_mut();
180 let set = PySet::empty(py)?;
180 let set = PySet::empty(py)?;
181 for path in inner_shared.iter_other_parent_paths() {
181 for path in inner_shared.iter_other_parent_paths() {
182 set.add(py, PyBytes::new(py, path.as_bytes()))?;
182 set.add(py, PyBytes::new(py, path.as_bytes()))?;
183 }
183 }
184 Ok(set.into_object())
184 Ok(set.into_object())
185 }
185 }
186
186
187 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
187 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
188 NonNormalEntries::from_inner(py, self.clone_ref(py))
188 NonNormalEntries::from_inner(py, self.clone_ref(py))
189 }
189 }
190
190
191 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
191 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
192 let key = key.extract::<PyBytes>(py)?;
192 let key = key.extract::<PyBytes>(py)?;
193 Ok(self
193 Ok(self
194 .inner(py)
194 .inner(py)
195 .borrow_mut()
195 .borrow_mut()
196 .non_normal_entries_contains(HgPath::new(key.data(py))))
196 .non_normal_entries_contains(HgPath::new(key.data(py))))
197 }
197 }
198
198
199 def non_normal_entries_display(&self) -> PyResult<PyString> {
199 def non_normal_entries_display(&self) -> PyResult<PyString> {
200 Ok(
200 Ok(
201 PyString::new(
201 PyString::new(
202 py,
202 py,
203 &format!(
203 &format!(
204 "NonNormalEntries: {}",
204 "NonNormalEntries: {}",
205 hg::utils::join_display(
205 hg::utils::join_display(
206 self
206 self
207 .inner(py)
207 .inner(py)
208 .borrow_mut()
208 .borrow_mut()
209 .iter_non_normal_paths(),
209 .iter_non_normal_paths(),
210 ", "
210 ", "
211 )
211 )
212 )
212 )
213 )
213 )
214 )
214 )
215 }
215 }
216
216
217 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
217 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
218 let key = key.extract::<PyBytes>(py)?;
218 let key = key.extract::<PyBytes>(py)?;
219 self
219 self
220 .inner(py)
220 .inner(py)
221 .borrow_mut()
221 .borrow_mut()
222 .non_normal_entries_remove(HgPath::new(key.data(py)));
222 .non_normal_entries_remove(HgPath::new(key.data(py)));
223 Ok(py.None())
223 Ok(py.None())
224 }
224 }
225
225
226 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
226 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
227 let mut inner = self.inner(py).borrow_mut();
227 let mut inner = self.inner(py).borrow_mut();
228
228
229 let ret = PyList::new(py, &[]);
229 let ret = PyList::new(py, &[]);
230 for filename in inner.non_normal_or_other_parent_paths() {
230 for filename in inner.non_normal_or_other_parent_paths() {
231 let as_pystring = PyBytes::new(py, filename.as_bytes());
231 let as_pystring = PyBytes::new(py, filename.as_bytes());
232 ret.append(py, as_pystring.into_object());
232 ret.append(py, as_pystring.into_object());
233 }
233 }
234 Ok(ret)
234 Ok(ret)
235 }
235 }
236
236
237 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
237 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
238 // Make sure the sets are defined before we no longer have a mutable
238 // Make sure the sets are defined before we no longer have a mutable
239 // reference to the dmap.
239 // reference to the dmap.
240 self.inner(py)
240 self.inner(py)
241 .borrow_mut()
241 .borrow_mut()
242 .set_non_normal_other_parent_entries(false);
242 .set_non_normal_other_parent_entries(false);
243
243
244 let leaked_ref = self.inner(py).leak_immutable();
244 let leaked_ref = self.inner(py).leak_immutable();
245
245
246 NonNormalEntriesIterator::from_inner(py, unsafe {
246 NonNormalEntriesIterator::from_inner(py, unsafe {
247 leaked_ref.map(py, |o| {
247 leaked_ref.map(py, |o| {
248 o.iter_non_normal_paths_panic()
248 o.iter_non_normal_paths_panic()
249 })
249 })
250 })
250 })
251 }
251 }
252
252
253 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
253 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
254 let d = d.extract::<PyBytes>(py)?;
254 let d = d.extract::<PyBytes>(py)?;
255 Ok(self.inner(py).borrow_mut()
255 Ok(self.inner(py).borrow_mut()
256 .has_tracked_dir(HgPath::new(d.data(py)))
256 .has_tracked_dir(HgPath::new(d.data(py)))
257 .map_err(|e| {
257 .map_err(|e| {
258 PyErr::new::<exc::ValueError, _>(py, e.to_string())
258 PyErr::new::<exc::ValueError, _>(py, e.to_string())
259 })?
259 })?
260 .to_py_object(py))
260 .to_py_object(py))
261 }
261 }
262
262
263 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
263 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
264 let d = d.extract::<PyBytes>(py)?;
264 let d = d.extract::<PyBytes>(py)?;
265 Ok(self.inner(py).borrow_mut()
265 Ok(self.inner(py).borrow_mut()
266 .has_dir(HgPath::new(d.data(py)))
266 .has_dir(HgPath::new(d.data(py)))
267 .map_err(|e| {
267 .map_err(|e| {
268 PyErr::new::<exc::ValueError, _>(py, e.to_string())
268 PyErr::new::<exc::ValueError, _>(py, e.to_string())
269 })?
269 })?
270 .to_py_object(py))
270 .to_py_object(py))
271 }
271 }
272
272
273 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
273 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
274 self.inner(py).borrow_mut()
274 self.inner(py).borrow_mut()
275 .parents(st.extract::<PyBytes>(py)?.data(py))
275 .parents(st.extract::<PyBytes>(py)?.data(py))
276 .map(|parents| dirstate_parents_to_pytuple(py, parents))
276 .map(|parents| dirstate_parents_to_pytuple(py, parents))
277 .or_else(|_| {
277 .or_else(|_| {
278 Err(PyErr::new::<exc::OSError, _>(
278 Err(PyErr::new::<exc::OSError, _>(
279 py,
279 py,
280 "Dirstate error".to_string(),
280 "Dirstate error".to_string(),
281 ))
281 ))
282 })
282 })
283 }
283 }
284
284
285 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
285 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
286 let p1 = extract_node_id(py, &p1)?;
286 let p1 = extract_node_id(py, &p1)?;
287 let p2 = extract_node_id(py, &p2)?;
287 let p2 = extract_node_id(py, &p2)?;
288
288
289 self.inner(py).borrow_mut()
289 self.inner(py).borrow_mut()
290 .set_parents(&DirstateParents { p1, p2 });
290 .set_parents(&DirstateParents { p1, p2 });
291 Ok(py.None())
291 Ok(py.None())
292 }
292 }
293
293
294 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
294 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
295 match self.inner(py).borrow_mut()
295 match self.inner(py).borrow_mut()
296 .read(st.extract::<PyBytes>(py)?.data(py))
296 .read(st.extract::<PyBytes>(py)?.data(py))
297 {
297 {
298 Ok(Some(parents)) => Ok(Some(
298 Ok(Some(parents)) => Ok(Some(
299 dirstate_parents_to_pytuple(py, parents)
299 dirstate_parents_to_pytuple(py, parents)
300 .into_object()
300 .into_object()
301 )),
301 )),
302 Ok(None) => Ok(Some(py.None())),
302 Ok(None) => Ok(Some(py.None())),
303 Err(_) => Err(PyErr::new::<exc::OSError, _>(
303 Err(_) => Err(PyErr::new::<exc::OSError, _>(
304 py,
304 py,
305 "Dirstate error".to_string(),
305 "Dirstate error".to_string(),
306 )),
306 )),
307 }
307 }
308 }
308 }
309 def write(
309 def write(
310 &self,
310 &self,
311 p1: PyObject,
311 p1: PyObject,
312 p2: PyObject,
312 p2: PyObject,
313 now: PyObject
313 now: PyObject
314 ) -> PyResult<PyBytes> {
314 ) -> PyResult<PyBytes> {
315 let now = Duration::new(now.extract(py)?, 0);
315 let now = Timestamp(now.extract(py)?);
316 let parents = DirstateParents {
316 let parents = DirstateParents {
317 p1: extract_node_id(py, &p1)?,
317 p1: extract_node_id(py, &p1)?,
318 p2: extract_node_id(py, &p2)?,
318 p2: extract_node_id(py, &p2)?,
319 };
319 };
320
320
321 match self.inner(py).borrow_mut().pack(parents, now) {
321 match self.inner(py).borrow_mut().pack(parents, now) {
322 Ok(packed) => Ok(PyBytes::new(py, &packed)),
322 Ok(packed) => Ok(PyBytes::new(py, &packed)),
323 Err(_) => Err(PyErr::new::<exc::OSError, _>(
323 Err(_) => Err(PyErr::new::<exc::OSError, _>(
324 py,
324 py,
325 "Dirstate error".to_string(),
325 "Dirstate error".to_string(),
326 )),
326 )),
327 }
327 }
328 }
328 }
329
329
330 def filefoldmapasdict(&self) -> PyResult<PyDict> {
330 def filefoldmapasdict(&self) -> PyResult<PyDict> {
331 let dict = PyDict::new(py);
331 let dict = PyDict::new(py);
332 for (key, value) in
332 for (key, value) in
333 self.inner(py).borrow_mut().build_file_fold_map().iter()
333 self.inner(py).borrow_mut().build_file_fold_map().iter()
334 {
334 {
335 dict.set_item(
335 dict.set_item(
336 py,
336 py,
337 PyBytes::new(py, key.as_bytes()).into_object(),
337 PyBytes::new(py, key.as_bytes()).into_object(),
338 PyBytes::new(py, value.as_bytes()).into_object(),
338 PyBytes::new(py, value.as_bytes()).into_object(),
339 )?;
339 )?;
340 }
340 }
341 Ok(dict)
341 Ok(dict)
342 }
342 }
343
343
344 def __len__(&self) -> PyResult<usize> {
344 def __len__(&self) -> PyResult<usize> {
345 Ok(self.inner(py).borrow().len())
345 Ok(self.inner(py).borrow().len())
346 }
346 }
347
347
348 def __contains__(&self, key: PyObject) -> PyResult<bool> {
348 def __contains__(&self, key: PyObject) -> PyResult<bool> {
349 let key = key.extract::<PyBytes>(py)?;
349 let key = key.extract::<PyBytes>(py)?;
350 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
350 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
351 }
351 }
352
352
353 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
353 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
354 let key = key.extract::<PyBytes>(py)?;
354 let key = key.extract::<PyBytes>(py)?;
355 let key = HgPath::new(key.data(py));
355 let key = HgPath::new(key.data(py));
356 match self.inner(py).borrow().get(key) {
356 match self.inner(py).borrow().get(key) {
357 Some(entry) => {
357 Some(entry) => {
358 Ok(make_dirstate_tuple(py, entry)?)
358 Ok(make_dirstate_tuple(py, entry)?)
359 },
359 },
360 None => Err(PyErr::new::<exc::KeyError, _>(
360 None => Err(PyErr::new::<exc::KeyError, _>(
361 py,
361 py,
362 String::from_utf8_lossy(key.as_bytes()),
362 String::from_utf8_lossy(key.as_bytes()),
363 )),
363 )),
364 }
364 }
365 }
365 }
366
366
367 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
367 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
368 let leaked_ref = self.inner(py).leak_immutable();
368 let leaked_ref = self.inner(py).leak_immutable();
369 DirstateMapKeysIterator::from_inner(
369 DirstateMapKeysIterator::from_inner(
370 py,
370 py,
371 unsafe { leaked_ref.map(py, |o| o.iter()) },
371 unsafe { leaked_ref.map(py, |o| o.iter()) },
372 )
372 )
373 }
373 }
374
374
375 def items(&self) -> PyResult<DirstateMapItemsIterator> {
375 def items(&self) -> PyResult<DirstateMapItemsIterator> {
376 let leaked_ref = self.inner(py).leak_immutable();
376 let leaked_ref = self.inner(py).leak_immutable();
377 DirstateMapItemsIterator::from_inner(
377 DirstateMapItemsIterator::from_inner(
378 py,
378 py,
379 unsafe { leaked_ref.map(py, |o| o.iter()) },
379 unsafe { leaked_ref.map(py, |o| o.iter()) },
380 )
380 )
381 }
381 }
382
382
383 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
383 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
384 let leaked_ref = self.inner(py).leak_immutable();
384 let leaked_ref = self.inner(py).leak_immutable();
385 DirstateMapKeysIterator::from_inner(
385 DirstateMapKeysIterator::from_inner(
386 py,
386 py,
387 unsafe { leaked_ref.map(py, |o| o.iter()) },
387 unsafe { leaked_ref.map(py, |o| o.iter()) },
388 )
388 )
389 }
389 }
390
390
391 def getdirs(&self) -> PyResult<Dirs> {
391 def getdirs(&self) -> PyResult<Dirs> {
392 // TODO don't copy, share the reference
392 // TODO don't copy, share the reference
393 self.inner(py).borrow_mut().set_dirs()
393 self.inner(py).borrow_mut().set_dirs()
394 .map_err(|e| {
394 .map_err(|e| {
395 PyErr::new::<exc::ValueError, _>(py, e.to_string())
395 PyErr::new::<exc::ValueError, _>(py, e.to_string())
396 })?;
396 })?;
397 Dirs::from_inner(
397 Dirs::from_inner(
398 py,
398 py,
399 DirsMultiset::from_dirstate(
399 DirsMultiset::from_dirstate(
400 self.inner(py).borrow().iter(),
400 self.inner(py).borrow().iter(),
401 Some(EntryState::Removed),
401 Some(EntryState::Removed),
402 )
402 )
403 .map_err(|e| {
403 .map_err(|e| {
404 PyErr::new::<exc::ValueError, _>(py, e.to_string())
404 PyErr::new::<exc::ValueError, _>(py, e.to_string())
405 })?,
405 })?,
406 )
406 )
407 }
407 }
408 def getalldirs(&self) -> PyResult<Dirs> {
408 def getalldirs(&self) -> PyResult<Dirs> {
409 // TODO don't copy, share the reference
409 // TODO don't copy, share the reference
410 self.inner(py).borrow_mut().set_all_dirs()
410 self.inner(py).borrow_mut().set_all_dirs()
411 .map_err(|e| {
411 .map_err(|e| {
412 PyErr::new::<exc::ValueError, _>(py, e.to_string())
412 PyErr::new::<exc::ValueError, _>(py, e.to_string())
413 })?;
413 })?;
414 Dirs::from_inner(
414 Dirs::from_inner(
415 py,
415 py,
416 DirsMultiset::from_dirstate(
416 DirsMultiset::from_dirstate(
417 self.inner(py).borrow().iter(),
417 self.inner(py).borrow().iter(),
418 None,
418 None,
419 ).map_err(|e| {
419 ).map_err(|e| {
420 PyErr::new::<exc::ValueError, _>(py, e.to_string())
420 PyErr::new::<exc::ValueError, _>(py, e.to_string())
421 })?,
421 })?,
422 )
422 )
423 }
423 }
424
424
425 // TODO all copymap* methods, see docstring above
425 // TODO all copymap* methods, see docstring above
426 def copymapcopy(&self) -> PyResult<PyDict> {
426 def copymapcopy(&self) -> PyResult<PyDict> {
427 let dict = PyDict::new(py);
427 let dict = PyDict::new(py);
428 for (key, value) in self.inner(py).borrow().copy_map_iter() {
428 for (key, value) in self.inner(py).borrow().copy_map_iter() {
429 dict.set_item(
429 dict.set_item(
430 py,
430 py,
431 PyBytes::new(py, key.as_bytes()),
431 PyBytes::new(py, key.as_bytes()),
432 PyBytes::new(py, value.as_bytes()),
432 PyBytes::new(py, value.as_bytes()),
433 )?;
433 )?;
434 }
434 }
435 Ok(dict)
435 Ok(dict)
436 }
436 }
437
437
438 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
438 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
439 let key = key.extract::<PyBytes>(py)?;
439 let key = key.extract::<PyBytes>(py)?;
440 match self.inner(py).borrow().copy_map_get(HgPath::new(key.data(py))) {
440 match self.inner(py).borrow().copy_map_get(HgPath::new(key.data(py))) {
441 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
441 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
442 None => Err(PyErr::new::<exc::KeyError, _>(
442 None => Err(PyErr::new::<exc::KeyError, _>(
443 py,
443 py,
444 String::from_utf8_lossy(key.data(py)),
444 String::from_utf8_lossy(key.data(py)),
445 )),
445 )),
446 }
446 }
447 }
447 }
448 def copymap(&self) -> PyResult<CopyMap> {
448 def copymap(&self) -> PyResult<CopyMap> {
449 CopyMap::from_inner(py, self.clone_ref(py))
449 CopyMap::from_inner(py, self.clone_ref(py))
450 }
450 }
451
451
452 def copymaplen(&self) -> PyResult<usize> {
452 def copymaplen(&self) -> PyResult<usize> {
453 Ok(self.inner(py).borrow().copy_map_len())
453 Ok(self.inner(py).borrow().copy_map_len())
454 }
454 }
455 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
455 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
456 let key = key.extract::<PyBytes>(py)?;
456 let key = key.extract::<PyBytes>(py)?;
457 Ok(self
457 Ok(self
458 .inner(py)
458 .inner(py)
459 .borrow()
459 .borrow()
460 .copy_map_contains_key(HgPath::new(key.data(py))))
460 .copy_map_contains_key(HgPath::new(key.data(py))))
461 }
461 }
462 def copymapget(
462 def copymapget(
463 &self,
463 &self,
464 key: PyObject,
464 key: PyObject,
465 default: Option<PyObject>
465 default: Option<PyObject>
466 ) -> PyResult<Option<PyObject>> {
466 ) -> PyResult<Option<PyObject>> {
467 let key = key.extract::<PyBytes>(py)?;
467 let key = key.extract::<PyBytes>(py)?;
468 match self
468 match self
469 .inner(py)
469 .inner(py)
470 .borrow()
470 .borrow()
471 .copy_map_get(HgPath::new(key.data(py)))
471 .copy_map_get(HgPath::new(key.data(py)))
472 {
472 {
473 Some(copy) => Ok(Some(
473 Some(copy) => Ok(Some(
474 PyBytes::new(py, copy.as_bytes()).into_object(),
474 PyBytes::new(py, copy.as_bytes()).into_object(),
475 )),
475 )),
476 None => Ok(default),
476 None => Ok(default),
477 }
477 }
478 }
478 }
479 def copymapsetitem(
479 def copymapsetitem(
480 &self,
480 &self,
481 key: PyObject,
481 key: PyObject,
482 value: PyObject
482 value: PyObject
483 ) -> PyResult<PyObject> {
483 ) -> PyResult<PyObject> {
484 let key = key.extract::<PyBytes>(py)?;
484 let key = key.extract::<PyBytes>(py)?;
485 let value = value.extract::<PyBytes>(py)?;
485 let value = value.extract::<PyBytes>(py)?;
486 self.inner(py).borrow_mut().copy_map_insert(
486 self.inner(py).borrow_mut().copy_map_insert(
487 HgPathBuf::from_bytes(key.data(py)),
487 HgPathBuf::from_bytes(key.data(py)),
488 HgPathBuf::from_bytes(value.data(py)),
488 HgPathBuf::from_bytes(value.data(py)),
489 );
489 );
490 Ok(py.None())
490 Ok(py.None())
491 }
491 }
492 def copymappop(
492 def copymappop(
493 &self,
493 &self,
494 key: PyObject,
494 key: PyObject,
495 default: Option<PyObject>
495 default: Option<PyObject>
496 ) -> PyResult<Option<PyObject>> {
496 ) -> PyResult<Option<PyObject>> {
497 let key = key.extract::<PyBytes>(py)?;
497 let key = key.extract::<PyBytes>(py)?;
498 match self
498 match self
499 .inner(py)
499 .inner(py)
500 .borrow_mut()
500 .borrow_mut()
501 .copy_map_remove(HgPath::new(key.data(py)))
501 .copy_map_remove(HgPath::new(key.data(py)))
502 {
502 {
503 Some(_) => Ok(None),
503 Some(_) => Ok(None),
504 None => Ok(default),
504 None => Ok(default),
505 }
505 }
506 }
506 }
507
507
508 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
508 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
509 let leaked_ref = self.inner(py).leak_immutable();
509 let leaked_ref = self.inner(py).leak_immutable();
510 CopyMapKeysIterator::from_inner(
510 CopyMapKeysIterator::from_inner(
511 py,
511 py,
512 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
512 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
513 )
513 )
514 }
514 }
515
515
516 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
516 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
517 let leaked_ref = self.inner(py).leak_immutable();
517 let leaked_ref = self.inner(py).leak_immutable();
518 CopyMapItemsIterator::from_inner(
518 CopyMapItemsIterator::from_inner(
519 py,
519 py,
520 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
520 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
521 )
521 )
522 }
522 }
523
523
524 });
524 });
525
525
526 impl DirstateMap {
526 impl DirstateMap {
527 pub fn get_inner<'a>(
527 pub fn get_inner<'a>(
528 &'a self,
528 &'a self,
529 py: Python<'a>,
529 py: Python<'a>,
530 ) -> Ref<'a, Box<dyn DirstateMapMethods + Send>> {
530 ) -> Ref<'a, Box<dyn DirstateMapMethods + Send>> {
531 self.inner(py).borrow()
531 self.inner(py).borrow()
532 }
532 }
533 fn translate_key(
533 fn translate_key(
534 py: Python,
534 py: Python,
535 res: (&HgPathBuf, &DirstateEntry),
535 res: (&HgPathBuf, &DirstateEntry),
536 ) -> PyResult<Option<PyBytes>> {
536 ) -> PyResult<Option<PyBytes>> {
537 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
537 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
538 }
538 }
539 fn translate_key_value(
539 fn translate_key_value(
540 py: Python,
540 py: Python,
541 res: (&HgPathBuf, &DirstateEntry),
541 res: (&HgPathBuf, &DirstateEntry),
542 ) -> PyResult<Option<(PyBytes, PyObject)>> {
542 ) -> PyResult<Option<(PyBytes, PyObject)>> {
543 let (f, entry) = res;
543 let (f, entry) = res;
544 Ok(Some((
544 Ok(Some((
545 PyBytes::new(py, f.as_bytes()),
545 PyBytes::new(py, f.as_bytes()),
546 make_dirstate_tuple(py, &entry)?,
546 make_dirstate_tuple(py, &entry)?,
547 )))
547 )))
548 }
548 }
549 }
549 }
550
550
551 py_shared_iterator!(
551 py_shared_iterator!(
552 DirstateMapKeysIterator,
552 DirstateMapKeysIterator,
553 UnsafePyLeaked<StateMapIter<'static>>,
553 UnsafePyLeaked<StateMapIter<'static>>,
554 DirstateMap::translate_key,
554 DirstateMap::translate_key,
555 Option<PyBytes>
555 Option<PyBytes>
556 );
556 );
557
557
558 py_shared_iterator!(
558 py_shared_iterator!(
559 DirstateMapItemsIterator,
559 DirstateMapItemsIterator,
560 UnsafePyLeaked<StateMapIter<'static>>,
560 UnsafePyLeaked<StateMapIter<'static>>,
561 DirstateMap::translate_key_value,
561 DirstateMap::translate_key_value,
562 Option<(PyBytes, PyObject)>
562 Option<(PyBytes, PyObject)>
563 );
563 );
564
564
565 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
565 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
566 let bytes = obj.extract::<PyBytes>(py)?;
566 let bytes = obj.extract::<PyBytes>(py)?;
567 match bytes.data(py).try_into() {
567 match bytes.data(py).try_into() {
568 Ok(s) => Ok(s),
568 Ok(s) => Ok(s),
569 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
569 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
570 }
570 }
571 }
571 }
@@ -1,163 +1,163
1 // parsers.rs
1 // parsers.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
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 //! Bindings for the `hg::dirstate::parsers` module provided by the
8 //! Bindings for the `hg::dirstate::parsers` module provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.parsers`
11 //! From Python, this will be seen as `mercurial.rustext.parsers`
12 use cpython::{
12 use cpython::{
13 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
13 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
14 PythonObject, ToPyObject,
14 PythonObject, ToPyObject,
15 };
15 };
16 use hg::{
16 use hg::{
17 pack_dirstate, parse_dirstate, utils::hg_path::HgPathBuf, DirstateEntry,
17 dirstate::parsers::Timestamp, pack_dirstate, parse_dirstate,
18 DirstateParents, FastHashMap, PARENT_SIZE,
18 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParents, FastHashMap,
19 PARENT_SIZE,
19 };
20 };
20 use std::convert::TryInto;
21 use std::convert::TryInto;
21
22
22 use crate::dirstate::{extract_dirstate, make_dirstate_tuple};
23 use crate::dirstate::{extract_dirstate, make_dirstate_tuple};
23 use std::time::Duration;
24
24
25 fn parse_dirstate_wrapper(
25 fn parse_dirstate_wrapper(
26 py: Python,
26 py: Python,
27 dmap: PyDict,
27 dmap: PyDict,
28 copymap: PyDict,
28 copymap: PyDict,
29 st: PyBytes,
29 st: PyBytes,
30 ) -> PyResult<PyTuple> {
30 ) -> PyResult<PyTuple> {
31 match parse_dirstate(st.data(py)) {
31 match parse_dirstate(st.data(py)) {
32 Ok((parents, entries, copies)) => {
32 Ok((parents, entries, copies)) => {
33 let dirstate_map: FastHashMap<HgPathBuf, DirstateEntry> = entries
33 let dirstate_map: FastHashMap<HgPathBuf, DirstateEntry> = entries
34 .into_iter()
34 .into_iter()
35 .map(|(path, entry)| (path.to_owned(), entry))
35 .map(|(path, entry)| (path.to_owned(), entry))
36 .collect();
36 .collect();
37 let copy_map: FastHashMap<HgPathBuf, HgPathBuf> = copies
37 let copy_map: FastHashMap<HgPathBuf, HgPathBuf> = copies
38 .into_iter()
38 .into_iter()
39 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
39 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
40 .collect();
40 .collect();
41
41
42 for (filename, entry) in &dirstate_map {
42 for (filename, entry) in &dirstate_map {
43 dmap.set_item(
43 dmap.set_item(
44 py,
44 py,
45 PyBytes::new(py, filename.as_bytes()),
45 PyBytes::new(py, filename.as_bytes()),
46 make_dirstate_tuple(py, entry)?,
46 make_dirstate_tuple(py, entry)?,
47 )?;
47 )?;
48 }
48 }
49 for (path, copy_path) in copy_map {
49 for (path, copy_path) in copy_map {
50 copymap.set_item(
50 copymap.set_item(
51 py,
51 py,
52 PyBytes::new(py, path.as_bytes()),
52 PyBytes::new(py, path.as_bytes()),
53 PyBytes::new(py, copy_path.as_bytes()),
53 PyBytes::new(py, copy_path.as_bytes()),
54 )?;
54 )?;
55 }
55 }
56 Ok(dirstate_parents_to_pytuple(py, parents))
56 Ok(dirstate_parents_to_pytuple(py, parents))
57 }
57 }
58 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
58 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
59 }
59 }
60 }
60 }
61
61
62 fn pack_dirstate_wrapper(
62 fn pack_dirstate_wrapper(
63 py: Python,
63 py: Python,
64 dmap: PyDict,
64 dmap: PyDict,
65 copymap: PyDict,
65 copymap: PyDict,
66 pl: PyTuple,
66 pl: PyTuple,
67 now: PyInt,
67 now: PyInt,
68 ) -> PyResult<PyBytes> {
68 ) -> PyResult<PyBytes> {
69 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
69 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
70 let p1: &[u8] = p1.data(py);
70 let p1: &[u8] = p1.data(py);
71 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
71 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
72 let p2: &[u8] = p2.data(py);
72 let p2: &[u8] = p2.data(py);
73
73
74 let mut dirstate_map = extract_dirstate(py, &dmap)?;
74 let mut dirstate_map = extract_dirstate(py, &dmap)?;
75
75
76 let copies: Result<FastHashMap<HgPathBuf, HgPathBuf>, PyErr> = copymap
76 let copies: Result<FastHashMap<HgPathBuf, HgPathBuf>, PyErr> = copymap
77 .items(py)
77 .items(py)
78 .iter()
78 .iter()
79 .map(|(key, value)| {
79 .map(|(key, value)| {
80 Ok((
80 Ok((
81 HgPathBuf::from_bytes(key.extract::<PyBytes>(py)?.data(py)),
81 HgPathBuf::from_bytes(key.extract::<PyBytes>(py)?.data(py)),
82 HgPathBuf::from_bytes(value.extract::<PyBytes>(py)?.data(py)),
82 HgPathBuf::from_bytes(value.extract::<PyBytes>(py)?.data(py)),
83 ))
83 ))
84 })
84 })
85 .collect();
85 .collect();
86
86
87 if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE {
87 if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE {
88 return Err(PyErr::new::<exc::ValueError, _>(
88 return Err(PyErr::new::<exc::ValueError, _>(
89 py,
89 py,
90 "expected a 20-byte hash".to_string(),
90 "expected a 20-byte hash".to_string(),
91 ));
91 ));
92 }
92 }
93
93
94 match pack_dirstate(
94 match pack_dirstate(
95 &mut dirstate_map,
95 &mut dirstate_map,
96 &copies?,
96 &copies?,
97 DirstateParents {
97 DirstateParents {
98 p1: p1.try_into().unwrap(),
98 p1: p1.try_into().unwrap(),
99 p2: p2.try_into().unwrap(),
99 p2: p2.try_into().unwrap(),
100 },
100 },
101 Duration::from_secs(now.as_object().extract::<u64>(py)?),
101 Timestamp(now.as_object().extract::<u64>(py)?),
102 ) {
102 ) {
103 Ok(packed) => {
103 Ok(packed) => {
104 for (filename, entry) in dirstate_map.iter() {
104 for (filename, entry) in dirstate_map.iter() {
105 dmap.set_item(
105 dmap.set_item(
106 py,
106 py,
107 PyBytes::new(py, filename.as_bytes()),
107 PyBytes::new(py, filename.as_bytes()),
108 make_dirstate_tuple(py, &entry)?,
108 make_dirstate_tuple(py, &entry)?,
109 )?;
109 )?;
110 }
110 }
111 Ok(PyBytes::new(py, &packed))
111 Ok(PyBytes::new(py, &packed))
112 }
112 }
113 Err(error) => {
113 Err(error) => {
114 Err(PyErr::new::<exc::ValueError, _>(py, error.to_string()))
114 Err(PyErr::new::<exc::ValueError, _>(py, error.to_string()))
115 }
115 }
116 }
116 }
117 }
117 }
118
118
119 /// Create the module, with `__package__` given from parent
119 /// Create the module, with `__package__` given from parent
120 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
120 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
121 let dotted_name = &format!("{}.parsers", package);
121 let dotted_name = &format!("{}.parsers", package);
122 let m = PyModule::new(py, dotted_name)?;
122 let m = PyModule::new(py, dotted_name)?;
123
123
124 m.add(py, "__package__", package)?;
124 m.add(py, "__package__", package)?;
125 m.add(py, "__doc__", "Parsers - Rust implementation")?;
125 m.add(py, "__doc__", "Parsers - Rust implementation")?;
126
126
127 m.add(
127 m.add(
128 py,
128 py,
129 "parse_dirstate",
129 "parse_dirstate",
130 py_fn!(
130 py_fn!(
131 py,
131 py,
132 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
132 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
133 ),
133 ),
134 )?;
134 )?;
135 m.add(
135 m.add(
136 py,
136 py,
137 "pack_dirstate",
137 "pack_dirstate",
138 py_fn!(
138 py_fn!(
139 py,
139 py,
140 pack_dirstate_wrapper(
140 pack_dirstate_wrapper(
141 dmap: PyDict,
141 dmap: PyDict,
142 copymap: PyDict,
142 copymap: PyDict,
143 pl: PyTuple,
143 pl: PyTuple,
144 now: PyInt
144 now: PyInt
145 )
145 )
146 ),
146 ),
147 )?;
147 )?;
148
148
149 let sys = PyModule::import(py, "sys")?;
149 let sys = PyModule::import(py, "sys")?;
150 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
150 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
151 sys_modules.set_item(py, dotted_name, &m)?;
151 sys_modules.set_item(py, dotted_name, &m)?;
152
152
153 Ok(m)
153 Ok(m)
154 }
154 }
155
155
156 pub(crate) fn dirstate_parents_to_pytuple(
156 pub(crate) fn dirstate_parents_to_pytuple(
157 py: Python,
157 py: Python,
158 parents: &DirstateParents,
158 parents: &DirstateParents,
159 ) -> PyTuple {
159 ) -> PyTuple {
160 let p1 = PyBytes::new(py, parents.p1.as_bytes());
160 let p1 = PyBytes::new(py, parents.p1.as_bytes());
161 let p2 = PyBytes::new(py, parents.p2.as_bytes());
161 let p2 = PyBytes::new(py, parents.p2.as_bytes());
162 (p1, p2).to_py_object(py)
162 (p1, p2).to_py_object(py)
163 }
163 }
General Comments 0
You need to be logged in to leave comments. Login now