##// END OF EJS Templates
rust-cpython: make `NonNormalEntires` iterable to fix `fsmonitor` (issue6276)...
Raphaël Gomès -
r44903:8ac5726d default
parent child Browse files
Show More
@@ -1,478 +1,497 b''
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::{
8 use crate::{
9 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
9 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
10 pack_dirstate, parse_dirstate,
10 pack_dirstate, parse_dirstate,
11 utils::{
11 utils::{
12 files::normalize_case,
12 files::normalize_case,
13 hg_path::{HgPath, HgPathBuf},
13 hg_path::{HgPath, HgPathBuf},
14 },
14 },
15 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
15 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
16 DirstateParents, DirstateParseError, FastHashMap, StateMap,
16 DirstateParents, DirstateParseError, FastHashMap, StateMap,
17 };
17 };
18 use core::borrow::Borrow;
18 use core::borrow::Borrow;
19 use std::collections::HashSet;
19 use std::collections::HashSet;
20 use std::convert::TryInto;
20 use std::convert::TryInto;
21 use std::iter::FromIterator;
21 use std::iter::FromIterator;
22 use std::ops::Deref;
22 use std::ops::Deref;
23 use std::time::Duration;
23 use std::time::Duration;
24
24
25 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
25 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
26
26
27 const NULL_ID: [u8; 20] = [0; 20];
27 const NULL_ID: [u8; 20] = [0; 20];
28 const MTIME_UNSET: i32 = -1;
28 const MTIME_UNSET: i32 = -1;
29
29
30 #[derive(Default)]
30 #[derive(Default)]
31 pub struct DirstateMap {
31 pub struct DirstateMap {
32 state_map: StateMap,
32 state_map: StateMap,
33 pub copy_map: CopyMap,
33 pub copy_map: CopyMap,
34 file_fold_map: Option<FileFoldMap>,
34 file_fold_map: Option<FileFoldMap>,
35 pub dirs: Option<DirsMultiset>,
35 pub dirs: Option<DirsMultiset>,
36 pub all_dirs: Option<DirsMultiset>,
36 pub all_dirs: Option<DirsMultiset>,
37 non_normal_set: Option<HashSet<HgPathBuf>>,
37 non_normal_set: Option<HashSet<HgPathBuf>>,
38 other_parent_set: Option<HashSet<HgPathBuf>>,
38 other_parent_set: Option<HashSet<HgPathBuf>>,
39 parents: Option<DirstateParents>,
39 parents: Option<DirstateParents>,
40 dirty_parents: bool,
40 dirty_parents: bool,
41 }
41 }
42
42
43 /// Should only really be used in python interface code, for clarity
43 /// Should only really be used in python interface code, for clarity
44 impl Deref for DirstateMap {
44 impl Deref for DirstateMap {
45 type Target = StateMap;
45 type Target = StateMap;
46
46
47 fn deref(&self) -> &Self::Target {
47 fn deref(&self) -> &Self::Target {
48 &self.state_map
48 &self.state_map
49 }
49 }
50 }
50 }
51
51
52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 iter: I,
54 iter: I,
55 ) -> Self {
55 ) -> Self {
56 Self {
56 Self {
57 state_map: iter.into_iter().collect(),
57 state_map: iter.into_iter().collect(),
58 ..Self::default()
58 ..Self::default()
59 }
59 }
60 }
60 }
61 }
61 }
62
62
63 impl DirstateMap {
63 impl DirstateMap {
64 pub fn new() -> Self {
64 pub fn new() -> Self {
65 Self::default()
65 Self::default()
66 }
66 }
67
67
68 pub fn clear(&mut self) {
68 pub fn clear(&mut self) {
69 self.state_map.clear();
69 self.state_map.clear();
70 self.copy_map.clear();
70 self.copy_map.clear();
71 self.file_fold_map = None;
71 self.file_fold_map = None;
72 self.non_normal_set = None;
72 self.non_normal_set = None;
73 self.other_parent_set = None;
73 self.other_parent_set = None;
74 self.set_parents(&DirstateParents {
74 self.set_parents(&DirstateParents {
75 p1: NULL_ID,
75 p1: NULL_ID,
76 p2: NULL_ID,
76 p2: NULL_ID,
77 })
77 })
78 }
78 }
79
79
80 /// Add a tracked file to the dirstate
80 /// Add a tracked file to the dirstate
81 pub fn add_file(
81 pub fn add_file(
82 &mut self,
82 &mut self,
83 filename: &HgPath,
83 filename: &HgPath,
84 old_state: EntryState,
84 old_state: EntryState,
85 entry: DirstateEntry,
85 entry: DirstateEntry,
86 ) -> Result<(), DirstateMapError> {
86 ) -> Result<(), DirstateMapError> {
87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 {
88 {
89 if let Some(ref mut dirs) = self.dirs {
89 if let Some(ref mut dirs) = self.dirs {
90 dirs.add_path(filename)?;
90 dirs.add_path(filename)?;
91 }
91 }
92 }
92 }
93 if old_state == EntryState::Unknown {
93 if old_state == EntryState::Unknown {
94 if let Some(ref mut all_dirs) = self.all_dirs {
94 if let Some(ref mut all_dirs) = self.all_dirs {
95 all_dirs.add_path(filename)?;
95 all_dirs.add_path(filename)?;
96 }
96 }
97 }
97 }
98 self.state_map.insert(filename.to_owned(), entry.to_owned());
98 self.state_map.insert(filename.to_owned(), entry.to_owned());
99
99
100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 self.get_non_normal_other_parent_entries()
101 self.get_non_normal_other_parent_entries()
102 .0
102 .0
103 .insert(filename.to_owned());
103 .insert(filename.to_owned());
104 }
104 }
105
105
106 if entry.size == SIZE_FROM_OTHER_PARENT {
106 if entry.size == SIZE_FROM_OTHER_PARENT {
107 self.get_non_normal_other_parent_entries()
107 self.get_non_normal_other_parent_entries()
108 .1
108 .1
109 .insert(filename.to_owned());
109 .insert(filename.to_owned());
110 }
110 }
111 Ok(())
111 Ok(())
112 }
112 }
113
113
114 /// Mark a file as removed in the dirstate.
114 /// Mark a file as removed in the dirstate.
115 ///
115 ///
116 /// The `size` parameter is used to store sentinel values that indicate
116 /// The `size` parameter is used to store sentinel values that indicate
117 /// the file's previous state. In the future, we should refactor this
117 /// the file's previous state. In the future, we should refactor this
118 /// to be more explicit about what that state is.
118 /// to be more explicit about what that state is.
119 pub fn remove_file(
119 pub fn remove_file(
120 &mut self,
120 &mut self,
121 filename: &HgPath,
121 filename: &HgPath,
122 old_state: EntryState,
122 old_state: EntryState,
123 size: i32,
123 size: i32,
124 ) -> Result<(), DirstateMapError> {
124 ) -> Result<(), DirstateMapError> {
125 if old_state != EntryState::Unknown && old_state != EntryState::Removed
125 if old_state != EntryState::Unknown && old_state != EntryState::Removed
126 {
126 {
127 if let Some(ref mut dirs) = self.dirs {
127 if let Some(ref mut dirs) = self.dirs {
128 dirs.delete_path(filename)?;
128 dirs.delete_path(filename)?;
129 }
129 }
130 }
130 }
131 if old_state == EntryState::Unknown {
131 if old_state == EntryState::Unknown {
132 if let Some(ref mut all_dirs) = self.all_dirs {
132 if let Some(ref mut all_dirs) = self.all_dirs {
133 all_dirs.add_path(filename)?;
133 all_dirs.add_path(filename)?;
134 }
134 }
135 }
135 }
136
136
137 if let Some(ref mut file_fold_map) = self.file_fold_map {
137 if let Some(ref mut file_fold_map) = self.file_fold_map {
138 file_fold_map.remove(&normalize_case(filename));
138 file_fold_map.remove(&normalize_case(filename));
139 }
139 }
140 self.state_map.insert(
140 self.state_map.insert(
141 filename.to_owned(),
141 filename.to_owned(),
142 DirstateEntry {
142 DirstateEntry {
143 state: EntryState::Removed,
143 state: EntryState::Removed,
144 mode: 0,
144 mode: 0,
145 size,
145 size,
146 mtime: 0,
146 mtime: 0,
147 },
147 },
148 );
148 );
149 self.get_non_normal_other_parent_entries()
149 self.get_non_normal_other_parent_entries()
150 .0
150 .0
151 .insert(filename.to_owned());
151 .insert(filename.to_owned());
152 Ok(())
152 Ok(())
153 }
153 }
154
154
155 /// Remove a file from the dirstate.
155 /// Remove a file from the dirstate.
156 /// Returns `true` if the file was previously recorded.
156 /// Returns `true` if the file was previously recorded.
157 pub fn drop_file(
157 pub fn drop_file(
158 &mut self,
158 &mut self,
159 filename: &HgPath,
159 filename: &HgPath,
160 old_state: EntryState,
160 old_state: EntryState,
161 ) -> Result<bool, DirstateMapError> {
161 ) -> Result<bool, DirstateMapError> {
162 let exists = self.state_map.remove(filename).is_some();
162 let exists = self.state_map.remove(filename).is_some();
163
163
164 if exists {
164 if exists {
165 if old_state != EntryState::Removed {
165 if old_state != EntryState::Removed {
166 if let Some(ref mut dirs) = self.dirs {
166 if let Some(ref mut dirs) = self.dirs {
167 dirs.delete_path(filename)?;
167 dirs.delete_path(filename)?;
168 }
168 }
169 }
169 }
170 if let Some(ref mut all_dirs) = self.all_dirs {
170 if let Some(ref mut all_dirs) = self.all_dirs {
171 all_dirs.delete_path(filename)?;
171 all_dirs.delete_path(filename)?;
172 }
172 }
173 }
173 }
174 if let Some(ref mut file_fold_map) = self.file_fold_map {
174 if let Some(ref mut file_fold_map) = self.file_fold_map {
175 file_fold_map.remove(&normalize_case(filename));
175 file_fold_map.remove(&normalize_case(filename));
176 }
176 }
177 self.get_non_normal_other_parent_entries()
177 self.get_non_normal_other_parent_entries()
178 .0
178 .0
179 .remove(filename);
179 .remove(filename);
180
180
181 Ok(exists)
181 Ok(exists)
182 }
182 }
183
183
184 pub fn clear_ambiguous_times(
184 pub fn clear_ambiguous_times(
185 &mut self,
185 &mut self,
186 filenames: Vec<HgPathBuf>,
186 filenames: Vec<HgPathBuf>,
187 now: i32,
187 now: i32,
188 ) {
188 ) {
189 for filename in filenames {
189 for filename in filenames {
190 let mut changed = false;
190 let mut changed = false;
191 self.state_map
191 self.state_map
192 .entry(filename.to_owned())
192 .entry(filename.to_owned())
193 .and_modify(|entry| {
193 .and_modify(|entry| {
194 if entry.state == EntryState::Normal && entry.mtime == now
194 if entry.state == EntryState::Normal && entry.mtime == now
195 {
195 {
196 changed = true;
196 changed = true;
197 *entry = DirstateEntry {
197 *entry = DirstateEntry {
198 mtime: MTIME_UNSET,
198 mtime: MTIME_UNSET,
199 ..*entry
199 ..*entry
200 };
200 };
201 }
201 }
202 });
202 });
203 if changed {
203 if changed {
204 self.get_non_normal_other_parent_entries()
204 self.get_non_normal_other_parent_entries()
205 .0
205 .0
206 .insert(filename.to_owned());
206 .insert(filename.to_owned());
207 }
207 }
208 }
208 }
209 }
209 }
210
210
211 pub fn non_normal_entries_remove(
211 pub fn non_normal_entries_remove(
212 &mut self,
212 &mut self,
213 key: impl AsRef<HgPath>,
213 key: impl AsRef<HgPath>,
214 ) -> bool {
214 ) -> bool {
215 self.get_non_normal_other_parent_entries()
215 self.get_non_normal_other_parent_entries()
216 .0
216 .0
217 .remove(key.as_ref())
217 .remove(key.as_ref())
218 }
218 }
219 pub fn non_normal_entries_union(
219 pub fn non_normal_entries_union(
220 &mut self,
220 &mut self,
221 other: HashSet<HgPathBuf>,
221 other: HashSet<HgPathBuf>,
222 ) -> Vec<HgPathBuf> {
222 ) -> Vec<HgPathBuf> {
223 self.get_non_normal_other_parent_entries()
223 self.get_non_normal_other_parent_entries()
224 .0
224 .0
225 .union(&other)
225 .union(&other)
226 .map(|e| e.to_owned())
226 .map(|e| e.to_owned())
227 .collect()
227 .collect()
228 }
228 }
229
229
230 pub fn get_non_normal_other_parent_entries(
230 pub fn get_non_normal_other_parent_entries(
231 &mut self,
231 &mut self,
232 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
232 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
233 self.set_non_normal_other_parent_entries(false);
233 self.set_non_normal_other_parent_entries(false);
234 (
234 (
235 self.non_normal_set.as_mut().unwrap(),
235 self.non_normal_set.as_mut().unwrap(),
236 self.other_parent_set.as_mut().unwrap(),
236 self.other_parent_set.as_mut().unwrap(),
237 )
237 )
238 }
238 }
239
239
240 /// Useful to get immutable references to those sets in contexts where
241 /// you only have an immutable reference to the `DirstateMap`, like when
242 /// sharing references with Python.
243 ///
244 /// TODO, get rid of this along with the other "setter/getter" stuff when
245 /// a nice typestate plan is defined.
246 ///
247 /// # Panics
248 ///
249 /// Will panic if either set is `None`.
250 pub fn get_non_normal_other_parent_entries_panic(
251 &self,
252 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
253 (
254 self.non_normal_set.as_ref().unwrap(),
255 self.other_parent_set.as_ref().unwrap(),
256 )
257 }
258
240 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
259 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
241 if !force
260 if !force
242 && self.non_normal_set.is_some()
261 && self.non_normal_set.is_some()
243 && self.other_parent_set.is_some()
262 && self.other_parent_set.is_some()
244 {
263 {
245 return;
264 return;
246 }
265 }
247 let mut non_normal = HashSet::new();
266 let mut non_normal = HashSet::new();
248 let mut other_parent = HashSet::new();
267 let mut other_parent = HashSet::new();
249
268
250 for (
269 for (
251 filename,
270 filename,
252 DirstateEntry {
271 DirstateEntry {
253 state, size, mtime, ..
272 state, size, mtime, ..
254 },
273 },
255 ) in self.state_map.iter()
274 ) in self.state_map.iter()
256 {
275 {
257 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
276 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
258 non_normal.insert(filename.to_owned());
277 non_normal.insert(filename.to_owned());
259 }
278 }
260 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
279 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
261 {
280 {
262 other_parent.insert(filename.to_owned());
281 other_parent.insert(filename.to_owned());
263 }
282 }
264 }
283 }
265 self.non_normal_set = Some(non_normal);
284 self.non_normal_set = Some(non_normal);
266 self.other_parent_set = Some(other_parent);
285 self.other_parent_set = Some(other_parent);
267 }
286 }
268
287
269 /// Both of these setters and their uses appear to be the simplest way to
288 /// Both of these setters and their uses appear to be the simplest way to
270 /// emulate a Python lazy property, but it is ugly and unidiomatic.
289 /// emulate a Python lazy property, but it is ugly and unidiomatic.
271 /// TODO One day, rewriting this struct using the typestate might be a
290 /// TODO One day, rewriting this struct using the typestate might be a
272 /// good idea.
291 /// good idea.
273 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
292 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
274 if self.all_dirs.is_none() {
293 if self.all_dirs.is_none() {
275 self.all_dirs =
294 self.all_dirs =
276 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
295 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
277 }
296 }
278 Ok(())
297 Ok(())
279 }
298 }
280
299
281 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
300 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
282 if self.dirs.is_none() {
301 if self.dirs.is_none() {
283 self.dirs = Some(DirsMultiset::from_dirstate(
302 self.dirs = Some(DirsMultiset::from_dirstate(
284 &self.state_map,
303 &self.state_map,
285 Some(EntryState::Removed),
304 Some(EntryState::Removed),
286 )?);
305 )?);
287 }
306 }
288 Ok(())
307 Ok(())
289 }
308 }
290
309
291 pub fn has_tracked_dir(
310 pub fn has_tracked_dir(
292 &mut self,
311 &mut self,
293 directory: &HgPath,
312 directory: &HgPath,
294 ) -> Result<bool, DirstateMapError> {
313 ) -> Result<bool, DirstateMapError> {
295 self.set_dirs()?;
314 self.set_dirs()?;
296 Ok(self.dirs.as_ref().unwrap().contains(directory))
315 Ok(self.dirs.as_ref().unwrap().contains(directory))
297 }
316 }
298
317
299 pub fn has_dir(
318 pub fn has_dir(
300 &mut self,
319 &mut self,
301 directory: &HgPath,
320 directory: &HgPath,
302 ) -> Result<bool, DirstateMapError> {
321 ) -> Result<bool, DirstateMapError> {
303 self.set_all_dirs()?;
322 self.set_all_dirs()?;
304 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
323 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
305 }
324 }
306
325
307 pub fn parents(
326 pub fn parents(
308 &mut self,
327 &mut self,
309 file_contents: &[u8],
328 file_contents: &[u8],
310 ) -> Result<&DirstateParents, DirstateError> {
329 ) -> Result<&DirstateParents, DirstateError> {
311 if let Some(ref parents) = self.parents {
330 if let Some(ref parents) = self.parents {
312 return Ok(parents);
331 return Ok(parents);
313 }
332 }
314 let parents;
333 let parents;
315 if file_contents.len() == PARENT_SIZE * 2 {
334 if file_contents.len() == PARENT_SIZE * 2 {
316 parents = DirstateParents {
335 parents = DirstateParents {
317 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
336 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
318 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
337 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
319 .try_into()
338 .try_into()
320 .unwrap(),
339 .unwrap(),
321 };
340 };
322 } else if file_contents.is_empty() {
341 } else if file_contents.is_empty() {
323 parents = DirstateParents {
342 parents = DirstateParents {
324 p1: NULL_ID,
343 p1: NULL_ID,
325 p2: NULL_ID,
344 p2: NULL_ID,
326 };
345 };
327 } else {
346 } else {
328 return Err(DirstateError::Parse(DirstateParseError::Damaged));
347 return Err(DirstateError::Parse(DirstateParseError::Damaged));
329 }
348 }
330
349
331 self.parents = Some(parents);
350 self.parents = Some(parents);
332 Ok(self.parents.as_ref().unwrap())
351 Ok(self.parents.as_ref().unwrap())
333 }
352 }
334
353
335 pub fn set_parents(&mut self, parents: &DirstateParents) {
354 pub fn set_parents(&mut self, parents: &DirstateParents) {
336 self.parents = Some(parents.clone());
355 self.parents = Some(parents.clone());
337 self.dirty_parents = true;
356 self.dirty_parents = true;
338 }
357 }
339
358
340 pub fn read(
359 pub fn read(
341 &mut self,
360 &mut self,
342 file_contents: &[u8],
361 file_contents: &[u8],
343 ) -> Result<Option<DirstateParents>, DirstateError> {
362 ) -> Result<Option<DirstateParents>, DirstateError> {
344 if file_contents.is_empty() {
363 if file_contents.is_empty() {
345 return Ok(None);
364 return Ok(None);
346 }
365 }
347
366
348 let parents = parse_dirstate(
367 let parents = parse_dirstate(
349 &mut self.state_map,
368 &mut self.state_map,
350 &mut self.copy_map,
369 &mut self.copy_map,
351 file_contents,
370 file_contents,
352 )?;
371 )?;
353
372
354 if !self.dirty_parents {
373 if !self.dirty_parents {
355 self.set_parents(&parents);
374 self.set_parents(&parents);
356 }
375 }
357
376
358 Ok(Some(parents))
377 Ok(Some(parents))
359 }
378 }
360
379
361 pub fn pack(
380 pub fn pack(
362 &mut self,
381 &mut self,
363 parents: DirstateParents,
382 parents: DirstateParents,
364 now: Duration,
383 now: Duration,
365 ) -> Result<Vec<u8>, DirstateError> {
384 ) -> Result<Vec<u8>, DirstateError> {
366 let packed =
385 let packed =
367 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
386 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
368
387
369 self.dirty_parents = false;
388 self.dirty_parents = false;
370
389
371 self.set_non_normal_other_parent_entries(true);
390 self.set_non_normal_other_parent_entries(true);
372 Ok(packed)
391 Ok(packed)
373 }
392 }
374
393
375 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
394 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
376 if let Some(ref file_fold_map) = self.file_fold_map {
395 if let Some(ref file_fold_map) = self.file_fold_map {
377 return file_fold_map;
396 return file_fold_map;
378 }
397 }
379 let mut new_file_fold_map = FileFoldMap::default();
398 let mut new_file_fold_map = FileFoldMap::default();
380 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
399 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
381 {
400 {
382 if *state == EntryState::Removed {
401 if *state == EntryState::Removed {
383 new_file_fold_map
402 new_file_fold_map
384 .insert(normalize_case(filename), filename.to_owned());
403 .insert(normalize_case(filename), filename.to_owned());
385 }
404 }
386 }
405 }
387 self.file_fold_map = Some(new_file_fold_map);
406 self.file_fold_map = Some(new_file_fold_map);
388 self.file_fold_map.as_ref().unwrap()
407 self.file_fold_map.as_ref().unwrap()
389 }
408 }
390 }
409 }
391
410
392 #[cfg(test)]
411 #[cfg(test)]
393 mod tests {
412 mod tests {
394 use super::*;
413 use super::*;
395
414
396 #[test]
415 #[test]
397 fn test_dirs_multiset() {
416 fn test_dirs_multiset() {
398 let mut map = DirstateMap::new();
417 let mut map = DirstateMap::new();
399 assert!(map.dirs.is_none());
418 assert!(map.dirs.is_none());
400 assert!(map.all_dirs.is_none());
419 assert!(map.all_dirs.is_none());
401
420
402 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
421 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
403 assert!(map.all_dirs.is_some());
422 assert!(map.all_dirs.is_some());
404 assert!(map.dirs.is_none());
423 assert!(map.dirs.is_none());
405
424
406 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
425 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
407 assert!(map.dirs.is_some());
426 assert!(map.dirs.is_some());
408 }
427 }
409
428
410 #[test]
429 #[test]
411 fn test_add_file() {
430 fn test_add_file() {
412 let mut map = DirstateMap::new();
431 let mut map = DirstateMap::new();
413
432
414 assert_eq!(0, map.len());
433 assert_eq!(0, map.len());
415
434
416 map.add_file(
435 map.add_file(
417 HgPath::new(b"meh"),
436 HgPath::new(b"meh"),
418 EntryState::Normal,
437 EntryState::Normal,
419 DirstateEntry {
438 DirstateEntry {
420 state: EntryState::Normal,
439 state: EntryState::Normal,
421 mode: 1337,
440 mode: 1337,
422 mtime: 1337,
441 mtime: 1337,
423 size: 1337,
442 size: 1337,
424 },
443 },
425 )
444 )
426 .unwrap();
445 .unwrap();
427
446
428 assert_eq!(1, map.len());
447 assert_eq!(1, map.len());
429 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
448 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
430 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
449 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
431 }
450 }
432
451
433 #[test]
452 #[test]
434 fn test_non_normal_other_parent_entries() {
453 fn test_non_normal_other_parent_entries() {
435 let mut map: DirstateMap = [
454 let mut map: DirstateMap = [
436 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
455 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
437 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
456 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
438 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
457 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
439 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
458 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
440 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
459 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
441 (b"f6", (EntryState::Added, 1337, 1337, -1)),
460 (b"f6", (EntryState::Added, 1337, 1337, -1)),
442 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
461 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
443 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
462 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
444 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
463 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
445 (b"fa", (EntryState::Added, 1337, -2, 1337)),
464 (b"fa", (EntryState::Added, 1337, -2, 1337)),
446 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
465 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
447 ]
466 ]
448 .iter()
467 .iter()
449 .map(|(fname, (state, mode, size, mtime))| {
468 .map(|(fname, (state, mode, size, mtime))| {
450 (
469 (
451 HgPathBuf::from_bytes(fname.as_ref()),
470 HgPathBuf::from_bytes(fname.as_ref()),
452 DirstateEntry {
471 DirstateEntry {
453 state: *state,
472 state: *state,
454 mode: *mode,
473 mode: *mode,
455 size: *size,
474 size: *size,
456 mtime: *mtime,
475 mtime: *mtime,
457 },
476 },
458 )
477 )
459 })
478 })
460 .collect();
479 .collect();
461
480
462 let mut non_normal = [
481 let mut non_normal = [
463 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
482 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
464 ]
483 ]
465 .iter()
484 .iter()
466 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
485 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
467 .collect();
486 .collect();
468
487
469 let mut other_parent = HashSet::new();
488 let mut other_parent = HashSet::new();
470 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
489 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
471 let entries = map.get_non_normal_other_parent_entries();
490 let entries = map.get_non_normal_other_parent_entries();
472
491
473 assert_eq!(
492 assert_eq!(
474 (&mut non_normal, &mut other_parent),
493 (&mut non_normal, &mut other_parent),
475 (entries.0, entries.1)
494 (entries.0, entries.1)
476 );
495 );
477 }
496 }
478 }
497 }
@@ -1,568 +1,586 b''
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;
13 use std::time::Duration;
14
14
15 use cpython::{
15 use cpython::{
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
18 UnsafePyLeaked,
18 UnsafePyLeaked,
19 };
19 };
20
20
21 use crate::{
21 use crate::{
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::non_normal_entries::NonNormalEntries,
23 dirstate::non_normal_entries::{
24 NonNormalEntries, NonNormalEntriesIterator,
25 },
24 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
26 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
25 };
27 };
26 use hg::{
28 use hg::{
27 utils::hg_path::{HgPath, HgPathBuf},
29 utils::hg_path::{HgPath, HgPathBuf},
28 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
30 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
29 DirstateMapError, DirstateParents, DirstateParseError, EntryState,
31 DirstateMapError, DirstateParents, DirstateParseError, EntryState,
30 StateMapIter, PARENT_SIZE,
32 StateMapIter, PARENT_SIZE,
31 };
33 };
32
34
33 // TODO
35 // TODO
34 // This object needs to share references to multiple members of its Rust
36 // This object needs to share references to multiple members of its Rust
35 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
36 // Right now `CopyMap` is done, but it needs to have an explicit reference
38 // Right now `CopyMap` is done, but it needs to have an explicit reference
37 // to `RustDirstateMap` which itself needs to have an encapsulation for
39 // to `RustDirstateMap` which itself needs to have an encapsulation for
38 // every method in `CopyMap` (copymapcopy, etc.).
40 // every method in `CopyMap` (copymapcopy, etc.).
39 // This is ugly and hard to maintain.
41 // This is ugly and hard to maintain.
40 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
41 // `py_class!` is already implemented and does not mention
43 // `py_class!` is already implemented and does not mention
42 // `RustDirstateMap`, rightfully so.
44 // `RustDirstateMap`, rightfully so.
43 // All attributes also have to have a separate refcount data attribute for
45 // All attributes also have to have a separate refcount data attribute for
44 // leaks, with all methods that go along for reference sharing.
46 // leaks, with all methods that go along for reference sharing.
45 py_class!(pub class DirstateMap |py| {
47 py_class!(pub class DirstateMap |py| {
46 @shared data inner: RustDirstateMap;
48 @shared data inner: RustDirstateMap;
47
49
48 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
50 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
49 let inner = RustDirstateMap::default();
51 let inner = RustDirstateMap::default();
50 Self::create_instance(py, inner)
52 Self::create_instance(py, inner)
51 }
53 }
52
54
53 def clear(&self) -> PyResult<PyObject> {
55 def clear(&self) -> PyResult<PyObject> {
54 self.inner(py).borrow_mut().clear();
56 self.inner(py).borrow_mut().clear();
55 Ok(py.None())
57 Ok(py.None())
56 }
58 }
57
59
58 def get(
60 def get(
59 &self,
61 &self,
60 key: PyObject,
62 key: PyObject,
61 default: Option<PyObject> = None
63 default: Option<PyObject> = None
62 ) -> PyResult<Option<PyObject>> {
64 ) -> PyResult<Option<PyObject>> {
63 let key = key.extract::<PyBytes>(py)?;
65 let key = key.extract::<PyBytes>(py)?;
64 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
66 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
65 Some(entry) => {
67 Some(entry) => {
66 Ok(Some(make_dirstate_tuple(py, entry)?))
68 Ok(Some(make_dirstate_tuple(py, entry)?))
67 },
69 },
68 None => Ok(default)
70 None => Ok(default)
69 }
71 }
70 }
72 }
71
73
72 def addfile(
74 def addfile(
73 &self,
75 &self,
74 f: PyObject,
76 f: PyObject,
75 oldstate: PyObject,
77 oldstate: PyObject,
76 state: PyObject,
78 state: PyObject,
77 mode: PyObject,
79 mode: PyObject,
78 size: PyObject,
80 size: PyObject,
79 mtime: PyObject
81 mtime: PyObject
80 ) -> PyResult<PyObject> {
82 ) -> PyResult<PyObject> {
81 self.inner(py).borrow_mut().add_file(
83 self.inner(py).borrow_mut().add_file(
82 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
84 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
83 oldstate.extract::<PyBytes>(py)?.data(py)[0]
85 oldstate.extract::<PyBytes>(py)?.data(py)[0]
84 .try_into()
86 .try_into()
85 .map_err(|e: DirstateParseError| {
87 .map_err(|e: DirstateParseError| {
86 PyErr::new::<exc::ValueError, _>(py, e.to_string())
88 PyErr::new::<exc::ValueError, _>(py, e.to_string())
87 })?,
89 })?,
88 DirstateEntry {
90 DirstateEntry {
89 state: state.extract::<PyBytes>(py)?.data(py)[0]
91 state: state.extract::<PyBytes>(py)?.data(py)[0]
90 .try_into()
92 .try_into()
91 .map_err(|e: DirstateParseError| {
93 .map_err(|e: DirstateParseError| {
92 PyErr::new::<exc::ValueError, _>(py, e.to_string())
94 PyErr::new::<exc::ValueError, _>(py, e.to_string())
93 })?,
95 })?,
94 mode: mode.extract(py)?,
96 mode: mode.extract(py)?,
95 size: size.extract(py)?,
97 size: size.extract(py)?,
96 mtime: mtime.extract(py)?,
98 mtime: mtime.extract(py)?,
97 },
99 },
98 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
100 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
99 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
101 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
100 })
102 })
101 }
103 }
102
104
103 def removefile(
105 def removefile(
104 &self,
106 &self,
105 f: PyObject,
107 f: PyObject,
106 oldstate: PyObject,
108 oldstate: PyObject,
107 size: PyObject
109 size: PyObject
108 ) -> PyResult<PyObject> {
110 ) -> PyResult<PyObject> {
109 self.inner(py).borrow_mut()
111 self.inner(py).borrow_mut()
110 .remove_file(
112 .remove_file(
111 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
113 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
112 oldstate.extract::<PyBytes>(py)?.data(py)[0]
114 oldstate.extract::<PyBytes>(py)?.data(py)[0]
113 .try_into()
115 .try_into()
114 .map_err(|e: DirstateParseError| {
116 .map_err(|e: DirstateParseError| {
115 PyErr::new::<exc::ValueError, _>(py, e.to_string())
117 PyErr::new::<exc::ValueError, _>(py, e.to_string())
116 })?,
118 })?,
117 size.extract(py)?,
119 size.extract(py)?,
118 )
120 )
119 .or_else(|_| {
121 .or_else(|_| {
120 Err(PyErr::new::<exc::OSError, _>(
122 Err(PyErr::new::<exc::OSError, _>(
121 py,
123 py,
122 "Dirstate error".to_string(),
124 "Dirstate error".to_string(),
123 ))
125 ))
124 })?;
126 })?;
125 Ok(py.None())
127 Ok(py.None())
126 }
128 }
127
129
128 def dropfile(
130 def dropfile(
129 &self,
131 &self,
130 f: PyObject,
132 f: PyObject,
131 oldstate: PyObject
133 oldstate: PyObject
132 ) -> PyResult<PyBool> {
134 ) -> PyResult<PyBool> {
133 self.inner(py).borrow_mut()
135 self.inner(py).borrow_mut()
134 .drop_file(
136 .drop_file(
135 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
137 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
136 oldstate.extract::<PyBytes>(py)?.data(py)[0]
138 oldstate.extract::<PyBytes>(py)?.data(py)[0]
137 .try_into()
139 .try_into()
138 .map_err(|e: DirstateParseError| {
140 .map_err(|e: DirstateParseError| {
139 PyErr::new::<exc::ValueError, _>(py, e.to_string())
141 PyErr::new::<exc::ValueError, _>(py, e.to_string())
140 })?,
142 })?,
141 )
143 )
142 .and_then(|b| Ok(b.to_py_object(py)))
144 .and_then(|b| Ok(b.to_py_object(py)))
143 .or_else(|_| {
145 .or_else(|_| {
144 Err(PyErr::new::<exc::OSError, _>(
146 Err(PyErr::new::<exc::OSError, _>(
145 py,
147 py,
146 "Dirstate error".to_string(),
148 "Dirstate error".to_string(),
147 ))
149 ))
148 })
150 })
149 }
151 }
150
152
151 def clearambiguoustimes(
153 def clearambiguoustimes(
152 &self,
154 &self,
153 files: PyObject,
155 files: PyObject,
154 now: PyObject
156 now: PyObject
155 ) -> PyResult<PyObject> {
157 ) -> PyResult<PyObject> {
156 let files: PyResult<Vec<HgPathBuf>> = files
158 let files: PyResult<Vec<HgPathBuf>> = files
157 .iter(py)?
159 .iter(py)?
158 .map(|filename| {
160 .map(|filename| {
159 Ok(HgPathBuf::from_bytes(
161 Ok(HgPathBuf::from_bytes(
160 filename?.extract::<PyBytes>(py)?.data(py),
162 filename?.extract::<PyBytes>(py)?.data(py),
161 ))
163 ))
162 })
164 })
163 .collect();
165 .collect();
164 self.inner(py).borrow_mut()
166 self.inner(py).borrow_mut()
165 .clear_ambiguous_times(files?, now.extract(py)?);
167 .clear_ambiguous_times(files?, now.extract(py)?);
166 Ok(py.None())
168 Ok(py.None())
167 }
169 }
168
170
169 def other_parent_entries(&self) -> PyResult<PyObject> {
171 def other_parent_entries(&self) -> PyResult<PyObject> {
170 let mut inner_shared = self.inner(py).borrow_mut();
172 let mut inner_shared = self.inner(py).borrow_mut();
171 let (_, other_parent) =
173 let (_, other_parent) =
172 inner_shared.get_non_normal_other_parent_entries();
174 inner_shared.get_non_normal_other_parent_entries();
173
175
174 let locals = PyDict::new(py);
176 let locals = PyDict::new(py);
175 locals.set_item(
177 locals.set_item(
176 py,
178 py,
177 "other_parent",
179 "other_parent",
178 other_parent
180 other_parent
179 .iter()
181 .iter()
180 .map(|v| PyBytes::new(py, v.as_ref()))
182 .map(|v| PyBytes::new(py, v.as_ref()))
181 .collect::<Vec<PyBytes>>()
183 .collect::<Vec<PyBytes>>()
182 .to_py_object(py),
184 .to_py_object(py),
183 )?;
185 )?;
184
186
185 py.eval("set(other_parent)", None, Some(&locals))
187 py.eval("set(other_parent)", None, Some(&locals))
186 }
188 }
187
189
188 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
190 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
189 NonNormalEntries::from_inner(py, self.clone_ref(py))
191 NonNormalEntries::from_inner(py, self.clone_ref(py))
190 }
192 }
191
193
192 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
194 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
193 let key = key.extract::<PyBytes>(py)?;
195 let key = key.extract::<PyBytes>(py)?;
194 Ok(self
196 Ok(self
195 .inner(py)
197 .inner(py)
196 .borrow_mut()
198 .borrow_mut()
197 .get_non_normal_other_parent_entries().0
199 .get_non_normal_other_parent_entries().0
198 .contains(HgPath::new(key.data(py))))
200 .contains(HgPath::new(key.data(py))))
199 }
201 }
200
202
201 def non_normal_entries_display(&self) -> PyResult<PyString> {
203 def non_normal_entries_display(&self) -> PyResult<PyString> {
202 Ok(
204 Ok(
203 PyString::new(
205 PyString::new(
204 py,
206 py,
205 &format!(
207 &format!(
206 "NonNormalEntries: {:?}",
208 "NonNormalEntries: {:?}",
207 self
209 self
208 .inner(py)
210 .inner(py)
209 .borrow_mut()
211 .borrow_mut()
210 .get_non_normal_other_parent_entries().0
212 .get_non_normal_other_parent_entries().0
211 .iter().map(|o| o))
213 .iter().map(|o| o))
212 )
214 )
213 )
215 )
214 }
216 }
215
217
216 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
218 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
217 let key = key.extract::<PyBytes>(py)?;
219 let key = key.extract::<PyBytes>(py)?;
218 self
220 self
219 .inner(py)
221 .inner(py)
220 .borrow_mut()
222 .borrow_mut()
221 .non_normal_entries_remove(HgPath::new(key.data(py)));
223 .non_normal_entries_remove(HgPath::new(key.data(py)));
222 Ok(py.None())
224 Ok(py.None())
223 }
225 }
224
226
225 def non_normal_entries_union(&self, other: PyObject) -> PyResult<PyList> {
227 def non_normal_entries_union(&self, other: PyObject) -> PyResult<PyList> {
226 let other: PyResult<_> = other.iter(py)?
228 let other: PyResult<_> = other.iter(py)?
227 .map(|f| {
229 .map(|f| {
228 Ok(HgPathBuf::from_bytes(
230 Ok(HgPathBuf::from_bytes(
229 f?.extract::<PyBytes>(py)?.data(py),
231 f?.extract::<PyBytes>(py)?.data(py),
230 ))
232 ))
231 })
233 })
232 .collect();
234 .collect();
233
235
234 let res = self
236 let res = self
235 .inner(py)
237 .inner(py)
236 .borrow_mut()
238 .borrow_mut()
237 .non_normal_entries_union(other?);
239 .non_normal_entries_union(other?);
238
240
239 let ret = PyList::new(py, &[]);
241 let ret = PyList::new(py, &[]);
240 for filename in res.iter() {
242 for filename in res.iter() {
241 let as_pystring = PyBytes::new(py, filename.as_bytes());
243 let as_pystring = PyBytes::new(py, filename.as_bytes());
242 ret.append(py, as_pystring.into_object());
244 ret.append(py, as_pystring.into_object());
243 }
245 }
244 Ok(ret)
246 Ok(ret)
245 }
247 }
246
248
249 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
250 // Make sure the sets are defined before we no longer have a mutable
251 // reference to the dmap.
252 self.inner(py)
253 .borrow_mut()
254 .set_non_normal_other_parent_entries(false);
255
256 let leaked_ref = self.inner(py).leak_immutable();
257
258 NonNormalEntriesIterator::from_inner(py, unsafe {
259 leaked_ref.map(py, |o| {
260 o.get_non_normal_other_parent_entries_panic().0.iter()
261 })
262 })
263 }
264
247 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
265 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
248 let d = d.extract::<PyBytes>(py)?;
266 let d = d.extract::<PyBytes>(py)?;
249 Ok(self.inner(py).borrow_mut()
267 Ok(self.inner(py).borrow_mut()
250 .has_tracked_dir(HgPath::new(d.data(py)))
268 .has_tracked_dir(HgPath::new(d.data(py)))
251 .map_err(|e| {
269 .map_err(|e| {
252 PyErr::new::<exc::ValueError, _>(py, e.to_string())
270 PyErr::new::<exc::ValueError, _>(py, e.to_string())
253 })?
271 })?
254 .to_py_object(py))
272 .to_py_object(py))
255 }
273 }
256
274
257 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
275 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
258 let d = d.extract::<PyBytes>(py)?;
276 let d = d.extract::<PyBytes>(py)?;
259 Ok(self.inner(py).borrow_mut()
277 Ok(self.inner(py).borrow_mut()
260 .has_dir(HgPath::new(d.data(py)))
278 .has_dir(HgPath::new(d.data(py)))
261 .map_err(|e| {
279 .map_err(|e| {
262 PyErr::new::<exc::ValueError, _>(py, e.to_string())
280 PyErr::new::<exc::ValueError, _>(py, e.to_string())
263 })?
281 })?
264 .to_py_object(py))
282 .to_py_object(py))
265 }
283 }
266
284
267 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
285 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
268 self.inner(py).borrow_mut()
286 self.inner(py).borrow_mut()
269 .parents(st.extract::<PyBytes>(py)?.data(py))
287 .parents(st.extract::<PyBytes>(py)?.data(py))
270 .and_then(|d| {
288 .and_then(|d| {
271 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
289 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
272 .to_py_object(py))
290 .to_py_object(py))
273 })
291 })
274 .or_else(|_| {
292 .or_else(|_| {
275 Err(PyErr::new::<exc::OSError, _>(
293 Err(PyErr::new::<exc::OSError, _>(
276 py,
294 py,
277 "Dirstate error".to_string(),
295 "Dirstate error".to_string(),
278 ))
296 ))
279 })
297 })
280 }
298 }
281
299
282 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
300 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
283 let p1 = extract_node_id(py, &p1)?;
301 let p1 = extract_node_id(py, &p1)?;
284 let p2 = extract_node_id(py, &p2)?;
302 let p2 = extract_node_id(py, &p2)?;
285
303
286 self.inner(py).borrow_mut()
304 self.inner(py).borrow_mut()
287 .set_parents(&DirstateParents { p1, p2 });
305 .set_parents(&DirstateParents { p1, p2 });
288 Ok(py.None())
306 Ok(py.None())
289 }
307 }
290
308
291 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
309 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
292 match self.inner(py).borrow_mut()
310 match self.inner(py).borrow_mut()
293 .read(st.extract::<PyBytes>(py)?.data(py))
311 .read(st.extract::<PyBytes>(py)?.data(py))
294 {
312 {
295 Ok(Some(parents)) => Ok(Some(
313 Ok(Some(parents)) => Ok(Some(
296 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
314 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
297 .to_py_object(py)
315 .to_py_object(py)
298 .into_object(),
316 .into_object(),
299 )),
317 )),
300 Ok(None) => Ok(Some(py.None())),
318 Ok(None) => Ok(Some(py.None())),
301 Err(_) => Err(PyErr::new::<exc::OSError, _>(
319 Err(_) => Err(PyErr::new::<exc::OSError, _>(
302 py,
320 py,
303 "Dirstate error".to_string(),
321 "Dirstate error".to_string(),
304 )),
322 )),
305 }
323 }
306 }
324 }
307 def write(
325 def write(
308 &self,
326 &self,
309 p1: PyObject,
327 p1: PyObject,
310 p2: PyObject,
328 p2: PyObject,
311 now: PyObject
329 now: PyObject
312 ) -> PyResult<PyBytes> {
330 ) -> PyResult<PyBytes> {
313 let now = Duration::new(now.extract(py)?, 0);
331 let now = Duration::new(now.extract(py)?, 0);
314 let parents = DirstateParents {
332 let parents = DirstateParents {
315 p1: extract_node_id(py, &p1)?,
333 p1: extract_node_id(py, &p1)?,
316 p2: extract_node_id(py, &p2)?,
334 p2: extract_node_id(py, &p2)?,
317 };
335 };
318
336
319 match self.inner(py).borrow_mut().pack(parents, now) {
337 match self.inner(py).borrow_mut().pack(parents, now) {
320 Ok(packed) => Ok(PyBytes::new(py, &packed)),
338 Ok(packed) => Ok(PyBytes::new(py, &packed)),
321 Err(_) => Err(PyErr::new::<exc::OSError, _>(
339 Err(_) => Err(PyErr::new::<exc::OSError, _>(
322 py,
340 py,
323 "Dirstate error".to_string(),
341 "Dirstate error".to_string(),
324 )),
342 )),
325 }
343 }
326 }
344 }
327
345
328 def filefoldmapasdict(&self) -> PyResult<PyDict> {
346 def filefoldmapasdict(&self) -> PyResult<PyDict> {
329 let dict = PyDict::new(py);
347 let dict = PyDict::new(py);
330 for (key, value) in
348 for (key, value) in
331 self.inner(py).borrow_mut().build_file_fold_map().iter()
349 self.inner(py).borrow_mut().build_file_fold_map().iter()
332 {
350 {
333 dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?;
351 dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?;
334 }
352 }
335 Ok(dict)
353 Ok(dict)
336 }
354 }
337
355
338 def __len__(&self) -> PyResult<usize> {
356 def __len__(&self) -> PyResult<usize> {
339 Ok(self.inner(py).borrow().len())
357 Ok(self.inner(py).borrow().len())
340 }
358 }
341
359
342 def __contains__(&self, key: PyObject) -> PyResult<bool> {
360 def __contains__(&self, key: PyObject) -> PyResult<bool> {
343 let key = key.extract::<PyBytes>(py)?;
361 let key = key.extract::<PyBytes>(py)?;
344 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
362 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
345 }
363 }
346
364
347 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
365 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
348 let key = key.extract::<PyBytes>(py)?;
366 let key = key.extract::<PyBytes>(py)?;
349 let key = HgPath::new(key.data(py));
367 let key = HgPath::new(key.data(py));
350 match self.inner(py).borrow().get(key) {
368 match self.inner(py).borrow().get(key) {
351 Some(entry) => {
369 Some(entry) => {
352 Ok(make_dirstate_tuple(py, entry)?)
370 Ok(make_dirstate_tuple(py, entry)?)
353 },
371 },
354 None => Err(PyErr::new::<exc::KeyError, _>(
372 None => Err(PyErr::new::<exc::KeyError, _>(
355 py,
373 py,
356 String::from_utf8_lossy(key.as_bytes()),
374 String::from_utf8_lossy(key.as_bytes()),
357 )),
375 )),
358 }
376 }
359 }
377 }
360
378
361 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
379 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
362 let leaked_ref = self.inner(py).leak_immutable();
380 let leaked_ref = self.inner(py).leak_immutable();
363 DirstateMapKeysIterator::from_inner(
381 DirstateMapKeysIterator::from_inner(
364 py,
382 py,
365 unsafe { leaked_ref.map(py, |o| o.iter()) },
383 unsafe { leaked_ref.map(py, |o| o.iter()) },
366 )
384 )
367 }
385 }
368
386
369 def items(&self) -> PyResult<DirstateMapItemsIterator> {
387 def items(&self) -> PyResult<DirstateMapItemsIterator> {
370 let leaked_ref = self.inner(py).leak_immutable();
388 let leaked_ref = self.inner(py).leak_immutable();
371 DirstateMapItemsIterator::from_inner(
389 DirstateMapItemsIterator::from_inner(
372 py,
390 py,
373 unsafe { leaked_ref.map(py, |o| o.iter()) },
391 unsafe { leaked_ref.map(py, |o| o.iter()) },
374 )
392 )
375 }
393 }
376
394
377 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
395 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
378 let leaked_ref = self.inner(py).leak_immutable();
396 let leaked_ref = self.inner(py).leak_immutable();
379 DirstateMapKeysIterator::from_inner(
397 DirstateMapKeysIterator::from_inner(
380 py,
398 py,
381 unsafe { leaked_ref.map(py, |o| o.iter()) },
399 unsafe { leaked_ref.map(py, |o| o.iter()) },
382 )
400 )
383 }
401 }
384
402
385 def getdirs(&self) -> PyResult<Dirs> {
403 def getdirs(&self) -> PyResult<Dirs> {
386 // TODO don't copy, share the reference
404 // TODO don't copy, share the reference
387 self.inner(py).borrow_mut().set_dirs()
405 self.inner(py).borrow_mut().set_dirs()
388 .map_err(|e| {
406 .map_err(|e| {
389 PyErr::new::<exc::ValueError, _>(py, e.to_string())
407 PyErr::new::<exc::ValueError, _>(py, e.to_string())
390 })?;
408 })?;
391 Dirs::from_inner(
409 Dirs::from_inner(
392 py,
410 py,
393 DirsMultiset::from_dirstate(
411 DirsMultiset::from_dirstate(
394 &self.inner(py).borrow(),
412 &self.inner(py).borrow(),
395 Some(EntryState::Removed),
413 Some(EntryState::Removed),
396 )
414 )
397 .map_err(|e| {
415 .map_err(|e| {
398 PyErr::new::<exc::ValueError, _>(py, e.to_string())
416 PyErr::new::<exc::ValueError, _>(py, e.to_string())
399 })?,
417 })?,
400 )
418 )
401 }
419 }
402 def getalldirs(&self) -> PyResult<Dirs> {
420 def getalldirs(&self) -> PyResult<Dirs> {
403 // TODO don't copy, share the reference
421 // TODO don't copy, share the reference
404 self.inner(py).borrow_mut().set_all_dirs()
422 self.inner(py).borrow_mut().set_all_dirs()
405 .map_err(|e| {
423 .map_err(|e| {
406 PyErr::new::<exc::ValueError, _>(py, e.to_string())
424 PyErr::new::<exc::ValueError, _>(py, e.to_string())
407 })?;
425 })?;
408 Dirs::from_inner(
426 Dirs::from_inner(
409 py,
427 py,
410 DirsMultiset::from_dirstate(
428 DirsMultiset::from_dirstate(
411 &self.inner(py).borrow(),
429 &self.inner(py).borrow(),
412 None,
430 None,
413 ).map_err(|e| {
431 ).map_err(|e| {
414 PyErr::new::<exc::ValueError, _>(py, e.to_string())
432 PyErr::new::<exc::ValueError, _>(py, e.to_string())
415 })?,
433 })?,
416 )
434 )
417 }
435 }
418
436
419 // TODO all copymap* methods, see docstring above
437 // TODO all copymap* methods, see docstring above
420 def copymapcopy(&self) -> PyResult<PyDict> {
438 def copymapcopy(&self) -> PyResult<PyDict> {
421 let dict = PyDict::new(py);
439 let dict = PyDict::new(py);
422 for (key, value) in self.inner(py).borrow().copy_map.iter() {
440 for (key, value) in self.inner(py).borrow().copy_map.iter() {
423 dict.set_item(
441 dict.set_item(
424 py,
442 py,
425 PyBytes::new(py, key.as_ref()),
443 PyBytes::new(py, key.as_ref()),
426 PyBytes::new(py, value.as_ref()),
444 PyBytes::new(py, value.as_ref()),
427 )?;
445 )?;
428 }
446 }
429 Ok(dict)
447 Ok(dict)
430 }
448 }
431
449
432 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
450 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
433 let key = key.extract::<PyBytes>(py)?;
451 let key = key.extract::<PyBytes>(py)?;
434 match self.inner(py).borrow().copy_map.get(HgPath::new(key.data(py))) {
452 match self.inner(py).borrow().copy_map.get(HgPath::new(key.data(py))) {
435 Some(copy) => Ok(PyBytes::new(py, copy.as_ref())),
453 Some(copy) => Ok(PyBytes::new(py, copy.as_ref())),
436 None => Err(PyErr::new::<exc::KeyError, _>(
454 None => Err(PyErr::new::<exc::KeyError, _>(
437 py,
455 py,
438 String::from_utf8_lossy(key.data(py)),
456 String::from_utf8_lossy(key.data(py)),
439 )),
457 )),
440 }
458 }
441 }
459 }
442 def copymap(&self) -> PyResult<CopyMap> {
460 def copymap(&self) -> PyResult<CopyMap> {
443 CopyMap::from_inner(py, self.clone_ref(py))
461 CopyMap::from_inner(py, self.clone_ref(py))
444 }
462 }
445
463
446 def copymaplen(&self) -> PyResult<usize> {
464 def copymaplen(&self) -> PyResult<usize> {
447 Ok(self.inner(py).borrow().copy_map.len())
465 Ok(self.inner(py).borrow().copy_map.len())
448 }
466 }
449 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
467 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
450 let key = key.extract::<PyBytes>(py)?;
468 let key = key.extract::<PyBytes>(py)?;
451 Ok(self
469 Ok(self
452 .inner(py)
470 .inner(py)
453 .borrow()
471 .borrow()
454 .copy_map
472 .copy_map
455 .contains_key(HgPath::new(key.data(py))))
473 .contains_key(HgPath::new(key.data(py))))
456 }
474 }
457 def copymapget(
475 def copymapget(
458 &self,
476 &self,
459 key: PyObject,
477 key: PyObject,
460 default: Option<PyObject>
478 default: Option<PyObject>
461 ) -> PyResult<Option<PyObject>> {
479 ) -> PyResult<Option<PyObject>> {
462 let key = key.extract::<PyBytes>(py)?;
480 let key = key.extract::<PyBytes>(py)?;
463 match self
481 match self
464 .inner(py)
482 .inner(py)
465 .borrow()
483 .borrow()
466 .copy_map
484 .copy_map
467 .get(HgPath::new(key.data(py)))
485 .get(HgPath::new(key.data(py)))
468 {
486 {
469 Some(copy) => Ok(Some(
487 Some(copy) => Ok(Some(
470 PyBytes::new(py, copy.as_ref()).into_object(),
488 PyBytes::new(py, copy.as_ref()).into_object(),
471 )),
489 )),
472 None => Ok(default),
490 None => Ok(default),
473 }
491 }
474 }
492 }
475 def copymapsetitem(
493 def copymapsetitem(
476 &self,
494 &self,
477 key: PyObject,
495 key: PyObject,
478 value: PyObject
496 value: PyObject
479 ) -> PyResult<PyObject> {
497 ) -> PyResult<PyObject> {
480 let key = key.extract::<PyBytes>(py)?;
498 let key = key.extract::<PyBytes>(py)?;
481 let value = value.extract::<PyBytes>(py)?;
499 let value = value.extract::<PyBytes>(py)?;
482 self.inner(py).borrow_mut().copy_map.insert(
500 self.inner(py).borrow_mut().copy_map.insert(
483 HgPathBuf::from_bytes(key.data(py)),
501 HgPathBuf::from_bytes(key.data(py)),
484 HgPathBuf::from_bytes(value.data(py)),
502 HgPathBuf::from_bytes(value.data(py)),
485 );
503 );
486 Ok(py.None())
504 Ok(py.None())
487 }
505 }
488 def copymappop(
506 def copymappop(
489 &self,
507 &self,
490 key: PyObject,
508 key: PyObject,
491 default: Option<PyObject>
509 default: Option<PyObject>
492 ) -> PyResult<Option<PyObject>> {
510 ) -> PyResult<Option<PyObject>> {
493 let key = key.extract::<PyBytes>(py)?;
511 let key = key.extract::<PyBytes>(py)?;
494 match self
512 match self
495 .inner(py)
513 .inner(py)
496 .borrow_mut()
514 .borrow_mut()
497 .copy_map
515 .copy_map
498 .remove(HgPath::new(key.data(py)))
516 .remove(HgPath::new(key.data(py)))
499 {
517 {
500 Some(_) => Ok(None),
518 Some(_) => Ok(None),
501 None => Ok(default),
519 None => Ok(default),
502 }
520 }
503 }
521 }
504
522
505 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
523 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
506 let leaked_ref = self.inner(py).leak_immutable();
524 let leaked_ref = self.inner(py).leak_immutable();
507 CopyMapKeysIterator::from_inner(
525 CopyMapKeysIterator::from_inner(
508 py,
526 py,
509 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
527 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
510 )
528 )
511 }
529 }
512
530
513 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
531 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
514 let leaked_ref = self.inner(py).leak_immutable();
532 let leaked_ref = self.inner(py).leak_immutable();
515 CopyMapItemsIterator::from_inner(
533 CopyMapItemsIterator::from_inner(
516 py,
534 py,
517 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
535 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
518 )
536 )
519 }
537 }
520
538
521 });
539 });
522
540
523 impl DirstateMap {
541 impl DirstateMap {
524 pub fn get_inner<'a>(
542 pub fn get_inner<'a>(
525 &'a self,
543 &'a self,
526 py: Python<'a>,
544 py: Python<'a>,
527 ) -> Ref<'a, RustDirstateMap> {
545 ) -> Ref<'a, RustDirstateMap> {
528 self.inner(py).borrow()
546 self.inner(py).borrow()
529 }
547 }
530 fn translate_key(
548 fn translate_key(
531 py: Python,
549 py: Python,
532 res: (&HgPathBuf, &DirstateEntry),
550 res: (&HgPathBuf, &DirstateEntry),
533 ) -> PyResult<Option<PyBytes>> {
551 ) -> PyResult<Option<PyBytes>> {
534 Ok(Some(PyBytes::new(py, res.0.as_ref())))
552 Ok(Some(PyBytes::new(py, res.0.as_ref())))
535 }
553 }
536 fn translate_key_value(
554 fn translate_key_value(
537 py: Python,
555 py: Python,
538 res: (&HgPathBuf, &DirstateEntry),
556 res: (&HgPathBuf, &DirstateEntry),
539 ) -> PyResult<Option<(PyBytes, PyObject)>> {
557 ) -> PyResult<Option<(PyBytes, PyObject)>> {
540 let (f, entry) = res;
558 let (f, entry) = res;
541 Ok(Some((
559 Ok(Some((
542 PyBytes::new(py, f.as_ref()),
560 PyBytes::new(py, f.as_ref()),
543 make_dirstate_tuple(py, entry)?,
561 make_dirstate_tuple(py, entry)?,
544 )))
562 )))
545 }
563 }
546 }
564 }
547
565
548 py_shared_iterator!(
566 py_shared_iterator!(
549 DirstateMapKeysIterator,
567 DirstateMapKeysIterator,
550 UnsafePyLeaked<StateMapIter<'static>>,
568 UnsafePyLeaked<StateMapIter<'static>>,
551 DirstateMap::translate_key,
569 DirstateMap::translate_key,
552 Option<PyBytes>
570 Option<PyBytes>
553 );
571 );
554
572
555 py_shared_iterator!(
573 py_shared_iterator!(
556 DirstateMapItemsIterator,
574 DirstateMapItemsIterator,
557 UnsafePyLeaked<StateMapIter<'static>>,
575 UnsafePyLeaked<StateMapIter<'static>>,
558 DirstateMap::translate_key_value,
576 DirstateMap::translate_key_value,
559 Option<(PyBytes, PyObject)>
577 Option<(PyBytes, PyObject)>
560 );
578 );
561
579
562 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
580 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
563 let bytes = obj.extract::<PyBytes>(py)?;
581 let bytes = obj.extract::<PyBytes>(py)?;
564 match bytes.data(py).try_into() {
582 match bytes.data(py).try_into() {
565 Ok(s) => Ok(s),
583 Ok(s) => Ok(s),
566 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
584 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
567 }
585 }
568 }
586 }
@@ -1,52 +1,76 b''
1 // non_normal_other_parent_entries.rs
1 // non_normal_other_parent_entries.rs
2 //
2 //
3 // Copyright 2020 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2020 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 cpython::{
8 use cpython::{
9 exc::NotImplementedError, CompareOp, ObjectProtocol, PyErr, PyList,
9 exc::NotImplementedError, CompareOp, ObjectProtocol, PyBytes, PyClone,
10 PyObject, PyResult, PyString, Python, PythonObject, ToPyObject,
10 PyErr, PyList, PyObject, PyResult, PyString, Python, PythonObject,
11 ToPyObject, UnsafePyLeaked,
11 };
12 };
12
13
13 use crate::dirstate::DirstateMap;
14 use crate::dirstate::DirstateMap;
15 use hg::utils::hg_path::HgPathBuf;
16 use std::cell::RefCell;
17 use std::collections::hash_set;
14
18
15 py_class!(pub class NonNormalEntries |py| {
19 py_class!(pub class NonNormalEntries |py| {
16 data dmap: DirstateMap;
20 data dmap: DirstateMap;
17
21
18 def __contains__(&self, key: PyObject) -> PyResult<bool> {
22 def __contains__(&self, key: PyObject) -> PyResult<bool> {
19 self.dmap(py).non_normal_entries_contains(py, key)
23 self.dmap(py).non_normal_entries_contains(py, key)
20 }
24 }
21 def remove(&self, key: PyObject) -> PyResult<PyObject> {
25 def remove(&self, key: PyObject) -> PyResult<PyObject> {
22 self.dmap(py).non_normal_entries_remove(py, key)
26 self.dmap(py).non_normal_entries_remove(py, key)
23 }
27 }
24 def union(&self, other: PyObject) -> PyResult<PyList> {
28 def union(&self, other: PyObject) -> PyResult<PyList> {
25 self.dmap(py).non_normal_entries_union(py, other)
29 self.dmap(py).non_normal_entries_union(py, other)
26 }
30 }
27 def __richcmp__(&self, other: PyObject, op: CompareOp) -> PyResult<bool> {
31 def __richcmp__(&self, other: PyObject, op: CompareOp) -> PyResult<bool> {
28 match op {
32 match op {
29 CompareOp::Eq => self.is_equal_to(py, other),
33 CompareOp::Eq => self.is_equal_to(py, other),
30 CompareOp::Ne => Ok(!self.is_equal_to(py, other)?),
34 CompareOp::Ne => Ok(!self.is_equal_to(py, other)?),
31 _ => Err(PyErr::new::<NotImplementedError, _>(py, ""))
35 _ => Err(PyErr::new::<NotImplementedError, _>(py, ""))
32 }
36 }
33 }
37 }
34 def __repr__(&self) -> PyResult<PyString> {
38 def __repr__(&self) -> PyResult<PyString> {
35 self.dmap(py).non_normal_entries_display(py)
39 self.dmap(py).non_normal_entries_display(py)
36 }
40 }
41
42 def __iter__(&self) -> PyResult<NonNormalEntriesIterator> {
43 self.dmap(py).non_normal_entries_iter(py)
44 }
37 });
45 });
38
46
39 impl NonNormalEntries {
47 impl NonNormalEntries {
40 pub fn from_inner(py: Python, dm: DirstateMap) -> PyResult<Self> {
48 pub fn from_inner(py: Python, dm: DirstateMap) -> PyResult<Self> {
41 Self::create_instance(py, dm)
49 Self::create_instance(py, dm)
42 }
50 }
43
51
44 fn is_equal_to(&self, py: Python, other: PyObject) -> PyResult<bool> {
52 fn is_equal_to(&self, py: Python, other: PyObject) -> PyResult<bool> {
45 for item in other.iter(py)? {
53 for item in other.iter(py)? {
46 if !self.dmap(py).non_normal_entries_contains(py, item?)? {
54 if !self.dmap(py).non_normal_entries_contains(py, item?)? {
47 return Ok(false);
55 return Ok(false);
48 }
56 }
49 }
57 }
50 Ok(true)
58 Ok(true)
51 }
59 }
60
61 fn translate_key(
62 py: Python,
63 key: &HgPathBuf,
64 ) -> PyResult<Option<PyBytes>> {
65 Ok(Some(PyBytes::new(py, key.as_ref())))
52 }
66 }
67 }
68
69 type NonNormalEntriesIter<'a> = hash_set::Iter<'a, HgPathBuf>;
70
71 py_shared_iterator!(
72 NonNormalEntriesIterator,
73 UnsafePyLeaked<NonNormalEntriesIter<'static>>,
74 NonNormalEntries::translate_key,
75 Option<PyBytes>
76 );
General Comments 0
You need to be logged in to leave comments. Login now