##// END OF EJS Templates
rust-dirs: handle forgotten `Result`s...
Raphaël Gomès -
r44315:bc7d8f45 default
parent child Browse files
Show More
@@ -1,341 +1,346 b''
1 1 // dirs_multiset.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! A multiset of directory names.
9 9 //!
10 10 //! Used to counts the references to directories in a manifest or dirstate.
11 11 use crate::utils::hg_path::{HgPath, HgPathBuf};
12 12 use crate::{
13 13 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
14 14 FastHashMap,
15 15 };
16 16 use std::collections::hash_map::{self, Entry};
17 17
18 18 // could be encapsulated if we care API stability more seriously
19 19 pub type DirsMultisetIter<'a> = hash_map::Keys<'a, HgPathBuf, u32>;
20 20
21 21 #[derive(PartialEq, Debug)]
22 22 pub struct DirsMultiset {
23 23 inner: FastHashMap<HgPathBuf, u32>,
24 24 }
25 25
26 26 impl DirsMultiset {
27 27 /// Initializes the multiset from a dirstate.
28 28 ///
29 29 /// If `skip_state` is provided, skips dirstate entries with equal state.
30 30 pub fn from_dirstate(
31 31 dirstate: &FastHashMap<HgPathBuf, DirstateEntry>,
32 32 skip_state: Option<EntryState>,
33 ) -> Self {
33 ) -> Result<Self, DirstateMapError> {
34 34 let mut multiset = DirsMultiset {
35 35 inner: FastHashMap::default(),
36 36 };
37 37
38 38 for (filename, DirstateEntry { state, .. }) in dirstate {
39 39 // This `if` is optimized out of the loop
40 40 if let Some(skip) = skip_state {
41 41 if skip != *state {
42 multiset.add_path(filename);
42 multiset.add_path(filename)?;
43 43 }
44 44 } else {
45 multiset.add_path(filename);
45 multiset.add_path(filename)?;
46 46 }
47 47 }
48 48
49 multiset
49 Ok(multiset)
50 50 }
51 51
52 52 /// Initializes the multiset from a manifest.
53 pub fn from_manifest(manifest: &[impl AsRef<HgPath>]) -> Self {
53 pub fn from_manifest(
54 manifest: &[impl AsRef<HgPath>],
55 ) -> Result<Self, DirstateMapError> {
54 56 let mut multiset = DirsMultiset {
55 57 inner: FastHashMap::default(),
56 58 };
57 59
58 60 for filename in manifest {
59 multiset.add_path(filename.as_ref());
61 multiset.add_path(filename.as_ref())?;
60 62 }
61 63
62 multiset
64 Ok(multiset)
63 65 }
64 66
65 67 /// Increases the count of deepest directory contained in the path.
66 68 ///
67 69 /// If the directory is not yet in the map, adds its parents.
68 70 pub fn add_path(
69 71 &mut self,
70 72 path: impl AsRef<HgPath>,
71 73 ) -> Result<(), DirstateMapError> {
72 74 for subpath in files::find_dirs(path.as_ref()) {
73 75 if subpath.as_bytes().last() == Some(&b'/') {
74 76 // TODO Remove this once PathAuditor is certified
75 77 // as the only entrypoint for path data
76 78 return Err(DirstateMapError::ConsecutiveSlashes);
77 79 }
78 80 if let Some(val) = self.inner.get_mut(subpath) {
79 81 *val += 1;
80 82 break;
81 83 }
82 84 self.inner.insert(subpath.to_owned(), 1);
83 85 }
84 86 Ok(())
85 87 }
86 88
87 89 /// Decreases the count of deepest directory contained in the path.
88 90 ///
89 91 /// If it is the only reference, decreases all parents until one is
90 92 /// removed.
91 93 /// If the directory is not in the map, something horrible has happened.
92 94 pub fn delete_path(
93 95 &mut self,
94 96 path: impl AsRef<HgPath>,
95 97 ) -> Result<(), DirstateMapError> {
96 98 for subpath in files::find_dirs(path.as_ref()) {
97 99 match self.inner.entry(subpath.to_owned()) {
98 100 Entry::Occupied(mut entry) => {
99 101 let val = entry.get().clone();
100 102 if val > 1 {
101 103 entry.insert(val - 1);
102 104 break;
103 105 }
104 106 entry.remove();
105 107 }
106 108 Entry::Vacant(_) => {
107 109 return Err(DirstateMapError::PathNotFound(
108 110 path.as_ref().to_owned(),
109 111 ))
110 112 }
111 113 };
112 114 }
113 115
114 116 Ok(())
115 117 }
116 118
117 119 pub fn contains(&self, key: impl AsRef<HgPath>) -> bool {
118 120 self.inner.contains_key(key.as_ref())
119 121 }
120 122
121 123 pub fn iter(&self) -> DirsMultisetIter {
122 124 self.inner.keys()
123 125 }
124 126
125 127 pub fn len(&self) -> usize {
126 128 self.inner.len()
127 129 }
128 130 }
129 131
130 132 #[cfg(test)]
131 133 mod tests {
132 134 use super::*;
133 135
134 136 #[test]
135 137 fn test_delete_path_path_not_found() {
136 138 let manifest: Vec<HgPathBuf> = vec![];
137 let mut map = DirsMultiset::from_manifest(&manifest);
139 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
138 140 let path = HgPathBuf::from_bytes(b"doesnotexist/");
139 141 assert_eq!(
140 142 Err(DirstateMapError::PathNotFound(path.to_owned())),
141 143 map.delete_path(&path)
142 144 );
143 145 }
144 146
145 147 #[test]
146 148 fn test_delete_path_empty_path() {
147 let mut map = DirsMultiset::from_manifest(&vec![HgPathBuf::new()]);
149 let mut map =
150 DirsMultiset::from_manifest(&vec![HgPathBuf::new()]).unwrap();
148 151 let path = HgPath::new(b"");
149 152 assert_eq!(Ok(()), map.delete_path(path));
150 153 assert_eq!(
151 154 Err(DirstateMapError::PathNotFound(path.to_owned())),
152 155 map.delete_path(path)
153 156 );
154 157 }
155 158
156 159 #[test]
157 160 fn test_delete_path_successful() {
158 161 let mut map = DirsMultiset {
159 162 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
160 163 .iter()
161 164 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
162 165 .collect(),
163 166 };
164 167
165 168 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
166 169 eprintln!("{:?}", map);
167 170 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
168 171 eprintln!("{:?}", map);
169 172 assert_eq!(
170 173 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
171 174 b"a/b/"
172 175 ))),
173 176 map.delete_path(HgPath::new(b"a/b/"))
174 177 );
175 178
176 179 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
177 180 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
178 181 eprintln!("{:?}", map);
179 182 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/")));
180 183 eprintln!("{:?}", map);
181 184
182 185 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/")));
183 186 assert_eq!(
184 187 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
185 188 b"a/c/"
186 189 ))),
187 190 map.delete_path(HgPath::new(b"a/c/"))
188 191 );
189 192 }
190 193
191 194 #[test]
192 195 fn test_add_path_empty_path() {
193 196 let manifest: Vec<HgPathBuf> = vec![];
194 let mut map = DirsMultiset::from_manifest(&manifest);
197 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
195 198 let path = HgPath::new(b"");
196 199 map.add_path(path);
197 200
198 201 assert_eq!(1, map.len());
199 202 }
200 203
201 204 #[test]
202 205 fn test_add_path_successful() {
203 206 let manifest: Vec<HgPathBuf> = vec![];
204 let mut map = DirsMultiset::from_manifest(&manifest);
207 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
205 208
206 209 map.add_path(HgPath::new(b"a/"));
207 210 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
208 211 assert_eq!(1, *map.inner.get(HgPath::new(b"")).unwrap());
209 212 assert_eq!(2, map.len());
210 213
211 214 // Non directory should be ignored
212 215 map.add_path(HgPath::new(b"a"));
213 216 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
214 217 assert_eq!(2, map.len());
215 218
216 219 // Non directory will still add its base
217 220 map.add_path(HgPath::new(b"a/b"));
218 221 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
219 222 assert_eq!(2, map.len());
220 223
221 224 // Duplicate path works
222 225 map.add_path(HgPath::new(b"a/"));
223 226 assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap());
224 227
225 228 // Nested dir adds to its base
226 229 map.add_path(HgPath::new(b"a/b/"));
227 230 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
228 231 assert_eq!(1, *map.inner.get(HgPath::new(b"a/b")).unwrap());
229 232
230 233 // but not its base's base, because it already existed
231 234 map.add_path(HgPath::new(b"a/b/c/"));
232 235 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
233 236 assert_eq!(2, *map.inner.get(HgPath::new(b"a/b")).unwrap());
234 237
235 238 map.add_path(HgPath::new(b"a/c/"));
236 239 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
237 240
238 241 let expected = DirsMultiset {
239 242 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
240 243 .iter()
241 244 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
242 245 .collect(),
243 246 };
244 247 assert_eq!(map, expected);
245 248 }
246 249
247 250 #[test]
248 251 fn test_dirsmultiset_new_empty() {
249 252 let manifest: Vec<HgPathBuf> = vec![];
250 let new = DirsMultiset::from_manifest(&manifest);
253 let new = DirsMultiset::from_manifest(&manifest).unwrap();
251 254 let expected = DirsMultiset {
252 255 inner: FastHashMap::default(),
253 256 };
254 257 assert_eq!(expected, new);
255 258
256 let new = DirsMultiset::from_dirstate(&FastHashMap::default(), None);
259 let new = DirsMultiset::from_dirstate(&FastHashMap::default(), None)
260 .unwrap();
257 261 let expected = DirsMultiset {
258 262 inner: FastHashMap::default(),
259 263 };
260 264 assert_eq!(expected, new);
261 265 }
262 266
263 267 #[test]
264 268 fn test_dirsmultiset_new_no_skip() {
265 269 let input_vec: Vec<HgPathBuf> = ["a/", "b/", "a/c", "a/d/"]
266 270 .iter()
267 271 .map(|e| HgPathBuf::from_bytes(e.as_bytes()))
268 272 .collect();
269 273 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
270 274 .iter()
271 275 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
272 276 .collect();
273 277
274 let new = DirsMultiset::from_manifest(&input_vec);
278 let new = DirsMultiset::from_manifest(&input_vec).unwrap();
275 279 let expected = DirsMultiset {
276 280 inner: expected_inner,
277 281 };
278 282 assert_eq!(expected, new);
279 283
280 284 let input_map = ["a/", "b/", "a/c", "a/d/"]
281 285 .iter()
282 286 .map(|f| {
283 287 (
284 288 HgPathBuf::from_bytes(f.as_bytes()),
285 289 DirstateEntry {
286 290 state: EntryState::Normal,
287 291 mode: 0,
288 292 mtime: 0,
289 293 size: 0,
290 294 },
291 295 )
292 296 })
293 297 .collect();
294 298 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
295 299 .iter()
296 300 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
297 301 .collect();
298 302
299 let new = DirsMultiset::from_dirstate(&input_map, None);
303 let new = DirsMultiset::from_dirstate(&input_map, None).unwrap();
300 304 let expected = DirsMultiset {
301 305 inner: expected_inner,
302 306 };
303 307 assert_eq!(expected, new);
304 308 }
305 309
306 310 #[test]
307 311 fn test_dirsmultiset_new_skip() {
308 312 let input_map = [
309 313 ("a/", EntryState::Normal),
310 314 ("a/b/", EntryState::Normal),
311 315 ("a/c", EntryState::Removed),
312 316 ("a/d/", EntryState::Merged),
313 317 ]
314 318 .iter()
315 319 .map(|(f, state)| {
316 320 (
317 321 HgPathBuf::from_bytes(f.as_bytes()),
318 322 DirstateEntry {
319 323 state: *state,
320 324 mode: 0,
321 325 mtime: 0,
322 326 size: 0,
323 327 },
324 328 )
325 329 })
326 330 .collect();
327 331
328 332 // "a" incremented with "a/c" and "a/d/"
329 333 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
330 334 .iter()
331 335 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
332 336 .collect();
333 337
334 338 let new =
335 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal));
339 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal))
340 .unwrap();
336 341 let expected = DirsMultiset {
337 342 inner: expected_inner,
338 343 };
339 344 assert_eq!(expected, new);
340 345 }
341 346 }
@@ -1,427 +1,435 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::{
9 9 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
10 10 pack_dirstate, parse_dirstate,
11 11 utils::{
12 12 files::normalize_case,
13 13 hg_path::{HgPath, HgPathBuf},
14 14 },
15 15 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
16 16 DirstateParents, DirstateParseError, FastHashMap, StateMap,
17 17 };
18 18 use core::borrow::Borrow;
19 19 use std::collections::HashSet;
20 20 use std::convert::TryInto;
21 21 use std::iter::FromIterator;
22 22 use std::ops::Deref;
23 23 use std::time::Duration;
24 24
25 25 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
26 26
27 27 const NULL_ID: [u8; 20] = [0; 20];
28 28 const MTIME_UNSET: i32 = -1;
29 29
30 30 #[derive(Default)]
31 31 pub struct DirstateMap {
32 32 state_map: StateMap,
33 33 pub copy_map: CopyMap,
34 34 file_fold_map: Option<FileFoldMap>,
35 35 pub dirs: Option<DirsMultiset>,
36 36 pub all_dirs: Option<DirsMultiset>,
37 37 non_normal_set: HashSet<HgPathBuf>,
38 38 other_parent_set: HashSet<HgPathBuf>,
39 39 parents: Option<DirstateParents>,
40 40 dirty_parents: bool,
41 41 }
42 42
43 43 /// Should only really be used in python interface code, for clarity
44 44 impl Deref for DirstateMap {
45 45 type Target = StateMap;
46 46
47 47 fn deref(&self) -> &Self::Target {
48 48 &self.state_map
49 49 }
50 50 }
51 51
52 52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 54 iter: I,
55 55 ) -> Self {
56 56 Self {
57 57 state_map: iter.into_iter().collect(),
58 58 ..Self::default()
59 59 }
60 60 }
61 61 }
62 62
63 63 impl DirstateMap {
64 64 pub fn new() -> Self {
65 65 Self::default()
66 66 }
67 67
68 68 pub fn clear(&mut self) {
69 69 self.state_map.clear();
70 70 self.copy_map.clear();
71 71 self.file_fold_map = None;
72 72 self.non_normal_set.clear();
73 73 self.other_parent_set.clear();
74 74 self.set_parents(&DirstateParents {
75 75 p1: NULL_ID,
76 76 p2: NULL_ID,
77 77 })
78 78 }
79 79
80 80 /// Add a tracked file to the dirstate
81 81 pub fn add_file(
82 82 &mut self,
83 83 filename: &HgPath,
84 84 old_state: EntryState,
85 85 entry: DirstateEntry,
86 86 ) -> Result<(), DirstateMapError> {
87 87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 88 {
89 89 if let Some(ref mut dirs) = self.dirs {
90 90 dirs.add_path(filename)?;
91 91 }
92 92 }
93 93 if old_state == EntryState::Unknown {
94 94 if let Some(ref mut all_dirs) = self.all_dirs {
95 95 all_dirs.add_path(filename)?;
96 96 }
97 97 }
98 98 self.state_map.insert(filename.to_owned(), entry.to_owned());
99 99
100 100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 101 self.non_normal_set.insert(filename.to_owned());
102 102 }
103 103
104 104 if entry.size == SIZE_FROM_OTHER_PARENT {
105 105 self.other_parent_set.insert(filename.to_owned());
106 106 }
107 107 Ok(())
108 108 }
109 109
110 110 /// Mark a file as removed in the dirstate.
111 111 ///
112 112 /// The `size` parameter is used to store sentinel values that indicate
113 113 /// the file's previous state. In the future, we should refactor this
114 114 /// to be more explicit about what that state is.
115 115 pub fn remove_file(
116 116 &mut self,
117 117 filename: &HgPath,
118 118 old_state: EntryState,
119 119 size: i32,
120 120 ) -> Result<(), DirstateMapError> {
121 121 if old_state != EntryState::Unknown && old_state != EntryState::Removed
122 122 {
123 123 if let Some(ref mut dirs) = self.dirs {
124 124 dirs.delete_path(filename)?;
125 125 }
126 126 }
127 127 if old_state == EntryState::Unknown {
128 128 if let Some(ref mut all_dirs) = self.all_dirs {
129 all_dirs.add_path(filename);
129 all_dirs.add_path(filename)?;
130 130 }
131 131 }
132 132
133 133 if let Some(ref mut file_fold_map) = self.file_fold_map {
134 134 file_fold_map.remove(&normalize_case(filename));
135 135 }
136 136 self.state_map.insert(
137 137 filename.to_owned(),
138 138 DirstateEntry {
139 139 state: EntryState::Removed,
140 140 mode: 0,
141 141 size,
142 142 mtime: 0,
143 143 },
144 144 );
145 145 self.non_normal_set.insert(filename.to_owned());
146 146 Ok(())
147 147 }
148 148
149 149 /// Remove a file from the dirstate.
150 150 /// Returns `true` if the file was previously recorded.
151 151 pub fn drop_file(
152 152 &mut self,
153 153 filename: &HgPath,
154 154 old_state: EntryState,
155 155 ) -> Result<bool, DirstateMapError> {
156 156 let exists = self.state_map.remove(filename).is_some();
157 157
158 158 if exists {
159 159 if old_state != EntryState::Removed {
160 160 if let Some(ref mut dirs) = self.dirs {
161 161 dirs.delete_path(filename)?;
162 162 }
163 163 }
164 164 if let Some(ref mut all_dirs) = self.all_dirs {
165 165 all_dirs.delete_path(filename)?;
166 166 }
167 167 }
168 168 if let Some(ref mut file_fold_map) = self.file_fold_map {
169 169 file_fold_map.remove(&normalize_case(filename));
170 170 }
171 171 self.non_normal_set.remove(filename);
172 172
173 173 Ok(exists)
174 174 }
175 175
176 176 pub fn clear_ambiguous_times(
177 177 &mut self,
178 178 filenames: Vec<HgPathBuf>,
179 179 now: i32,
180 180 ) {
181 181 for filename in filenames {
182 182 let mut changed = false;
183 183 self.state_map
184 184 .entry(filename.to_owned())
185 185 .and_modify(|entry| {
186 186 if entry.state == EntryState::Normal && entry.mtime == now
187 187 {
188 188 changed = true;
189 189 *entry = DirstateEntry {
190 190 mtime: MTIME_UNSET,
191 191 ..*entry
192 192 };
193 193 }
194 194 });
195 195 if changed {
196 196 self.non_normal_set.insert(filename.to_owned());
197 197 }
198 198 }
199 199 }
200 200
201 201 pub fn non_normal_other_parent_entries(
202 202 &self,
203 203 ) -> (HashSet<HgPathBuf>, HashSet<HgPathBuf>) {
204 204 let mut non_normal = HashSet::new();
205 205 let mut other_parent = HashSet::new();
206 206
207 207 for (
208 208 filename,
209 209 DirstateEntry {
210 210 state, size, mtime, ..
211 211 },
212 212 ) in self.state_map.iter()
213 213 {
214 214 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
215 215 non_normal.insert(filename.to_owned());
216 216 }
217 217 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
218 218 {
219 219 other_parent.insert(filename.to_owned());
220 220 }
221 221 }
222 222
223 223 (non_normal, other_parent)
224 224 }
225 225
226 226 /// Both of these setters and their uses appear to be the simplest way to
227 227 /// emulate a Python lazy property, but it is ugly and unidiomatic.
228 228 /// TODO One day, rewriting this struct using the typestate might be a
229 229 /// good idea.
230 pub fn set_all_dirs(&mut self) {
230 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
231 231 if self.all_dirs.is_none() {
232 232 self.all_dirs =
233 Some(DirsMultiset::from_dirstate(&self.state_map, None));
233 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
234 234 }
235 Ok(())
235 236 }
236 237
237 pub fn set_dirs(&mut self) {
238 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
238 239 if self.dirs.is_none() {
239 240 self.dirs = Some(DirsMultiset::from_dirstate(
240 241 &self.state_map,
241 242 Some(EntryState::Removed),
242 ));
243 )?);
243 244 }
245 Ok(())
244 246 }
245 247
246 pub fn has_tracked_dir(&mut self, directory: &HgPath) -> bool {
247 self.set_dirs();
248 self.dirs.as_ref().unwrap().contains(directory)
248 pub fn has_tracked_dir(
249 &mut self,
250 directory: &HgPath,
251 ) -> Result<bool, DirstateMapError> {
252 self.set_dirs()?;
253 Ok(self.dirs.as_ref().unwrap().contains(directory))
249 254 }
250 255
251 pub fn has_dir(&mut self, directory: &HgPath) -> bool {
252 self.set_all_dirs();
253 self.all_dirs.as_ref().unwrap().contains(directory)
256 pub fn has_dir(
257 &mut self,
258 directory: &HgPath,
259 ) -> Result<bool, DirstateMapError> {
260 self.set_all_dirs()?;
261 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
254 262 }
255 263
256 264 pub fn parents(
257 265 &mut self,
258 266 file_contents: &[u8],
259 267 ) -> Result<&DirstateParents, DirstateError> {
260 268 if let Some(ref parents) = self.parents {
261 269 return Ok(parents);
262 270 }
263 271 let parents;
264 272 if file_contents.len() == PARENT_SIZE * 2 {
265 273 parents = DirstateParents {
266 274 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
267 275 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
268 276 .try_into()
269 277 .unwrap(),
270 278 };
271 279 } else if file_contents.is_empty() {
272 280 parents = DirstateParents {
273 281 p1: NULL_ID,
274 282 p2: NULL_ID,
275 283 };
276 284 } else {
277 285 return Err(DirstateError::Parse(DirstateParseError::Damaged));
278 286 }
279 287
280 288 self.parents = Some(parents);
281 289 Ok(self.parents.as_ref().unwrap())
282 290 }
283 291
284 292 pub fn set_parents(&mut self, parents: &DirstateParents) {
285 293 self.parents = Some(parents.clone());
286 294 self.dirty_parents = true;
287 295 }
288 296
289 297 pub fn read(
290 298 &mut self,
291 299 file_contents: &[u8],
292 300 ) -> Result<Option<DirstateParents>, DirstateError> {
293 301 if file_contents.is_empty() {
294 302 return Ok(None);
295 303 }
296 304
297 305 let parents = parse_dirstate(
298 306 &mut self.state_map,
299 307 &mut self.copy_map,
300 308 file_contents,
301 309 )?;
302 310
303 311 if !self.dirty_parents {
304 312 self.set_parents(&parents);
305 313 }
306 314
307 315 Ok(Some(parents))
308 316 }
309 317
310 318 pub fn pack(
311 319 &mut self,
312 320 parents: DirstateParents,
313 321 now: Duration,
314 322 ) -> Result<Vec<u8>, DirstateError> {
315 323 let packed =
316 324 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
317 325
318 326 self.dirty_parents = false;
319 327
320 328 let result = self.non_normal_other_parent_entries();
321 329 self.non_normal_set = result.0;
322 330 self.other_parent_set = result.1;
323 331 Ok(packed)
324 332 }
325 333
326 334 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
327 335 if let Some(ref file_fold_map) = self.file_fold_map {
328 336 return file_fold_map;
329 337 }
330 338 let mut new_file_fold_map = FileFoldMap::default();
331 339 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
332 340 {
333 341 if *state == EntryState::Removed {
334 342 new_file_fold_map
335 343 .insert(normalize_case(filename), filename.to_owned());
336 344 }
337 345 }
338 346 self.file_fold_map = Some(new_file_fold_map);
339 347 self.file_fold_map.as_ref().unwrap()
340 348 }
341 349 }
342 350
343 351 #[cfg(test)]
344 352 mod tests {
345 353 use super::*;
346 354
347 355 #[test]
348 356 fn test_dirs_multiset() {
349 357 let mut map = DirstateMap::new();
350 358 assert!(map.dirs.is_none());
351 359 assert!(map.all_dirs.is_none());
352 360
353 assert_eq!(false, map.has_dir(HgPath::new(b"nope")));
361 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
354 362 assert!(map.all_dirs.is_some());
355 363 assert!(map.dirs.is_none());
356 364
357 assert_eq!(false, map.has_tracked_dir(HgPath::new(b"nope")));
365 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
358 366 assert!(map.dirs.is_some());
359 367 }
360 368
361 369 #[test]
362 370 fn test_add_file() {
363 371 let mut map = DirstateMap::new();
364 372
365 373 assert_eq!(0, map.len());
366 374
367 375 map.add_file(
368 376 HgPath::new(b"meh"),
369 377 EntryState::Normal,
370 378 DirstateEntry {
371 379 state: EntryState::Normal,
372 380 mode: 1337,
373 381 mtime: 1337,
374 382 size: 1337,
375 383 },
376 384 );
377 385
378 386 assert_eq!(1, map.len());
379 387 assert_eq!(0, map.non_normal_set.len());
380 388 assert_eq!(0, map.other_parent_set.len());
381 389 }
382 390
383 391 #[test]
384 392 fn test_non_normal_other_parent_entries() {
385 393 let map: DirstateMap = [
386 394 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
387 395 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
388 396 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
389 397 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
390 398 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
391 399 (b"f6", (EntryState::Added, 1337, 1337, -1)),
392 400 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
393 401 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
394 402 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
395 403 (b"fa", (EntryState::Added, 1337, -2, 1337)),
396 404 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
397 405 ]
398 406 .iter()
399 407 .map(|(fname, (state, mode, size, mtime))| {
400 408 (
401 409 HgPathBuf::from_bytes(fname.as_ref()),
402 410 DirstateEntry {
403 411 state: *state,
404 412 mode: *mode,
405 413 size: *size,
406 414 mtime: *mtime,
407 415 },
408 416 )
409 417 })
410 418 .collect();
411 419
412 420 let non_normal = [
413 421 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
414 422 ]
415 423 .iter()
416 424 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
417 425 .collect();
418 426
419 427 let mut other_parent = HashSet::new();
420 428 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
421 429
422 430 assert_eq!(
423 431 (non_normal, other_parent),
424 432 map.non_normal_other_parent_entries()
425 433 );
426 434 }
427 435 }
@@ -1,116 +1,116 b''
1 1 // matchers.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Structs and types for matching files and directories.
9 9
10 use crate::utils::hg_path::{HgPath, HgPathBuf};
10 use crate::utils::hg_path::HgPath;
11 11 use std::collections::HashSet;
12 12
13 13 pub enum VisitChildrenSet<'a> {
14 14 /// Don't visit anything
15 15 Empty,
16 16 /// Only visit this directory
17 17 This,
18 18 /// Visit this directory and these subdirectories
19 19 /// TODO Should we implement a `NonEmptyHashSet`?
20 20 Set(HashSet<&'a HgPath>),
21 21 /// Visit this directory and all subdirectories
22 22 Recursive,
23 23 }
24 24
25 25 pub trait Matcher {
26 26 /// Explicitly listed files
27 27 fn file_set(&self) -> Option<&HashSet<&HgPath>>;
28 28 /// Returns whether `filename` is in `file_set`
29 29 fn exact_match(&self, filename: impl AsRef<HgPath>) -> bool;
30 30 /// Returns whether `filename` is matched by this matcher
31 31 fn matches(&self, filename: impl AsRef<HgPath>) -> bool;
32 32 /// Decides whether a directory should be visited based on whether it
33 33 /// has potential matches in it or one of its subdirectories, and
34 34 /// potentially lists which subdirectories of that directory should be
35 35 /// visited. This is based on the match's primary, included, and excluded
36 36 /// patterns.
37 37 ///
38 38 /// # Example
39 39 ///
40 40 /// Assume matchers `['path:foo/bar', 'rootfilesin:qux']`, we would
41 41 /// return the following values (assuming the implementation of
42 42 /// visit_children_set is capable of recognizing this; some implementations
43 43 /// are not).
44 44 ///
45 45 /// ```ignore
46 46 /// '' -> {'foo', 'qux'}
47 47 /// 'baz' -> set()
48 48 /// 'foo' -> {'bar'}
49 49 /// // Ideally this would be `Recursive`, but since the prefix nature of
50 50 /// // matchers is applied to the entire matcher, we have to downgrade this
51 51 /// // to `This` due to the (yet to be implemented in Rust) non-prefix
52 52 /// // `RootFilesIn'-kind matcher being mixed in.
53 53 /// 'foo/bar' -> 'this'
54 54 /// 'qux' -> 'this'
55 55 /// ```
56 56 /// # Important
57 57 ///
58 58 /// Most matchers do not know if they're representing files or
59 59 /// directories. They see `['path:dir/f']` and don't know whether `f` is a
60 60 /// file or a directory, so `visit_children_set('dir')` for most matchers
61 61 /// will return `HashSet{ HgPath { "f" } }`, but if the matcher knows it's
62 62 /// a file (like the yet to be implemented in Rust `ExactMatcher` does),
63 63 /// it may return `VisitChildrenSet::This`.
64 64 /// Do not rely on the return being a `HashSet` indicating that there are
65 65 /// no files in this dir to investigate (or equivalently that if there are
66 66 /// files to investigate in 'dir' that it will always return
67 67 /// `VisitChildrenSet::This`).
68 68 fn visit_children_set(
69 69 &self,
70 70 directory: impl AsRef<HgPath>,
71 71 ) -> VisitChildrenSet;
72 72 /// Matcher will match everything and `files_set()` will be empty:
73 73 /// optimization might be possible.
74 74 fn matches_everything(&self) -> bool;
75 75 /// Matcher will match exactly the files in `files_set()`: optimization
76 76 /// might be possible.
77 77 fn is_exact(&self) -> bool;
78 78 }
79 79
80 80 /// Matches everything.
81 81 ///```
82 82 /// use hg::{ matchers::{Matcher, AlwaysMatcher}, utils::hg_path::HgPath };
83 83 ///
84 84 /// let matcher = AlwaysMatcher;
85 85 ///
86 86 /// assert_eq!(true, matcher.matches(HgPath::new(b"whatever")));
87 87 /// assert_eq!(true, matcher.matches(HgPath::new(b"b.txt")));
88 88 /// assert_eq!(true, matcher.matches(HgPath::new(b"main.c")));
89 89 /// assert_eq!(true, matcher.matches(HgPath::new(br"re:.*\.c$")));
90 90 /// ```
91 91 #[derive(Debug)]
92 92 pub struct AlwaysMatcher;
93 93
94 94 impl Matcher for AlwaysMatcher {
95 95 fn file_set(&self) -> Option<&HashSet<&HgPath>> {
96 96 None
97 97 }
98 98 fn exact_match(&self, _filename: impl AsRef<HgPath>) -> bool {
99 99 false
100 100 }
101 101 fn matches(&self, _filename: impl AsRef<HgPath>) -> bool {
102 102 true
103 103 }
104 104 fn visit_children_set(
105 105 &self,
106 106 _directory: impl AsRef<HgPath>,
107 107 ) -> VisitChildrenSet {
108 108 VisitChildrenSet::Recursive
109 109 }
110 110 fn matches_everything(&self) -> bool {
111 111 true
112 112 }
113 113 fn is_exact(&self) -> bool {
114 114 false
115 115 }
116 116 }
@@ -1,140 +1,146 b''
1 1 // dirs_multiset.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::RefCell;
12 12 use std::convert::TryInto;
13 13
14 14 use cpython::{
15 15 exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
16 16 Python,
17 17 };
18 18
19 19 use crate::dirstate::extract_dirstate;
20 20 use crate::ref_sharing::{PyLeaked, PySharedRefCell};
21 21 use hg::{
22 22 utils::hg_path::{HgPath, HgPathBuf},
23 23 DirsMultiset, DirsMultisetIter, DirstateMapError, DirstateParseError,
24 24 EntryState,
25 25 };
26 26
27 27 py_class!(pub class Dirs |py| {
28 28 data inner: PySharedRefCell<DirsMultiset>;
29 29
30 30 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
31 31 // a `list`)
32 32 def __new__(
33 33 _cls,
34 34 map: PyObject,
35 35 skip: Option<PyObject> = None
36 36 ) -> PyResult<Self> {
37 37 let mut skip_state: Option<EntryState> = None;
38 38 if let Some(skip) = skip {
39 39 skip_state = Some(
40 40 skip.extract::<PyBytes>(py)?.data(py)[0]
41 41 .try_into()
42 42 .map_err(|e: DirstateParseError| {
43 43 PyErr::new::<exc::ValueError, _>(py, e.to_string())
44 44 })?,
45 45 );
46 46 }
47 47 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
48 48 let dirstate = extract_dirstate(py, &map)?;
49 49 DirsMultiset::from_dirstate(&dirstate, skip_state)
50 .map_err(|e| {
51 PyErr::new::<exc::ValueError, _>(py, e.to_string())
52 })?
50 53 } else {
51 54 let map: Result<Vec<HgPathBuf>, PyErr> = map
52 55 .iter(py)?
53 56 .map(|o| {
54 57 Ok(HgPathBuf::from_bytes(
55 58 o?.extract::<PyBytes>(py)?.data(py),
56 59 ))
57 60 })
58 61 .collect();
59 62 DirsMultiset::from_manifest(&map?)
63 .map_err(|e| {
64 PyErr::new::<exc::ValueError, _>(py, e.to_string())
65 })?
60 66 };
61 67
62 68 Self::create_instance(
63 69 py,
64 70 PySharedRefCell::new(inner),
65 71 )
66 72 }
67 73
68 74 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
69 75 self.inner_shared(py).borrow_mut()?.add_path(
70 76 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
71 77 ).and(Ok(py.None())).or_else(|e| {
72 78 match e {
73 79 DirstateMapError::EmptyPath => {
74 80 Ok(py.None())
75 81 },
76 82 e => {
77 83 Err(PyErr::new::<exc::ValueError, _>(
78 84 py,
79 85 e.to_string(),
80 86 ))
81 87 }
82 88 }
83 89 })
84 90 }
85 91
86 92 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
87 93 self.inner_shared(py).borrow_mut()?.delete_path(
88 94 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
89 95 )
90 96 .and(Ok(py.None()))
91 97 .or_else(|e| {
92 98 match e {
93 99 DirstateMapError::EmptyPath => {
94 100 Ok(py.None())
95 101 },
96 102 e => {
97 103 Err(PyErr::new::<exc::ValueError, _>(
98 104 py,
99 105 e.to_string(),
100 106 ))
101 107 }
102 108 }
103 109 })
104 110 }
105 111 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
106 112 let leaked_ref = self.inner_shared(py).leak_immutable();
107 113 DirsMultisetKeysIterator::from_inner(
108 114 py,
109 115 unsafe { leaked_ref.map(py, |o| o.iter()) },
110 116 )
111 117 }
112 118
113 119 def __contains__(&self, item: PyObject) -> PyResult<bool> {
114 120 Ok(self.inner_shared(py).borrow().contains(HgPath::new(
115 121 item.extract::<PyBytes>(py)?.data(py).as_ref(),
116 122 )))
117 123 }
118 124 });
119 125
120 126 py_shared_ref!(Dirs, DirsMultiset, inner, inner_shared);
121 127
122 128 impl Dirs {
123 129 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
124 130 Self::create_instance(py, PySharedRefCell::new(d))
125 131 }
126 132
127 133 fn translate_key(
128 134 py: Python,
129 135 res: &HgPathBuf,
130 136 ) -> PyResult<Option<PyBytes>> {
131 137 Ok(Some(PyBytes::new(py, res.as_ref())))
132 138 }
133 139 }
134 140
135 141 py_shared_iterator!(
136 142 DirsMultisetKeysIterator,
137 143 PyLeaked<DirsMultisetIter<'static>>,
138 144 Dirs::translate_key,
139 145 Option<PyBytes>
140 146 );
@@ -1,505 +1,522 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::{Ref, RefCell};
12 12 use std::convert::TryInto;
13 13 use std::time::Duration;
14 14
15 15 use cpython::{
16 16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
17 17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
18 18 };
19 19
20 20 use crate::{
21 21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 22 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
23 23 ref_sharing::{PyLeaked, PySharedRefCell},
24 24 };
25 25 use hg::{
26 26 utils::hg_path::{HgPath, HgPathBuf},
27 27 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
28 28 DirstateMapError, DirstateParents, DirstateParseError, EntryState,
29 29 StateMapIter, PARENT_SIZE,
30 30 };
31 31
32 32 // TODO
33 33 // This object needs to share references to multiple members of its Rust
34 34 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
35 35 // Right now `CopyMap` is done, but it needs to have an explicit reference
36 36 // to `RustDirstateMap` which itself needs to have an encapsulation for
37 37 // every method in `CopyMap` (copymapcopy, etc.).
38 38 // This is ugly and hard to maintain.
39 39 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
40 40 // `py_class!` is already implemented and does not mention
41 41 // `RustDirstateMap`, rightfully so.
42 42 // All attributes also have to have a separate refcount data attribute for
43 43 // leaks, with all methods that go along for reference sharing.
44 44 py_class!(pub class DirstateMap |py| {
45 45 data inner: PySharedRefCell<RustDirstateMap>;
46 46
47 47 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
48 48 let inner = RustDirstateMap::default();
49 49 Self::create_instance(
50 50 py,
51 51 PySharedRefCell::new(inner),
52 52 )
53 53 }
54 54
55 55 def clear(&self) -> PyResult<PyObject> {
56 56 self.inner_shared(py).borrow_mut()?.clear();
57 57 Ok(py.None())
58 58 }
59 59
60 60 def get(
61 61 &self,
62 62 key: PyObject,
63 63 default: Option<PyObject> = None
64 64 ) -> PyResult<Option<PyObject>> {
65 65 let key = key.extract::<PyBytes>(py)?;
66 66 match self.inner_shared(py).borrow().get(HgPath::new(key.data(py))) {
67 67 Some(entry) => {
68 68 Ok(Some(make_dirstate_tuple(py, entry)?))
69 69 },
70 70 None => Ok(default)
71 71 }
72 72 }
73 73
74 74 def addfile(
75 75 &self,
76 76 f: PyObject,
77 77 oldstate: PyObject,
78 78 state: PyObject,
79 79 mode: PyObject,
80 80 size: PyObject,
81 81 mtime: PyObject
82 82 ) -> PyResult<PyObject> {
83 83 self.inner_shared(py).borrow_mut()?.add_file(
84 84 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
85 85 oldstate.extract::<PyBytes>(py)?.data(py)[0]
86 86 .try_into()
87 87 .map_err(|e: DirstateParseError| {
88 88 PyErr::new::<exc::ValueError, _>(py, e.to_string())
89 89 })?,
90 90 DirstateEntry {
91 91 state: state.extract::<PyBytes>(py)?.data(py)[0]
92 92 .try_into()
93 93 .map_err(|e: DirstateParseError| {
94 94 PyErr::new::<exc::ValueError, _>(py, e.to_string())
95 95 })?,
96 96 mode: mode.extract(py)?,
97 97 size: size.extract(py)?,
98 98 mtime: mtime.extract(py)?,
99 99 },
100 100 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
101 101 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
102 102 })
103 103 }
104 104
105 105 def removefile(
106 106 &self,
107 107 f: PyObject,
108 108 oldstate: PyObject,
109 109 size: PyObject
110 110 ) -> PyResult<PyObject> {
111 111 self.inner_shared(py).borrow_mut()?
112 112 .remove_file(
113 113 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
114 114 oldstate.extract::<PyBytes>(py)?.data(py)[0]
115 115 .try_into()
116 116 .map_err(|e: DirstateParseError| {
117 117 PyErr::new::<exc::ValueError, _>(py, e.to_string())
118 118 })?,
119 119 size.extract(py)?,
120 120 )
121 121 .or_else(|_| {
122 122 Err(PyErr::new::<exc::OSError, _>(
123 123 py,
124 124 "Dirstate error".to_string(),
125 125 ))
126 126 })?;
127 127 Ok(py.None())
128 128 }
129 129
130 130 def dropfile(
131 131 &self,
132 132 f: PyObject,
133 133 oldstate: PyObject
134 134 ) -> PyResult<PyBool> {
135 135 self.inner_shared(py).borrow_mut()?
136 136 .drop_file(
137 137 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
138 138 oldstate.extract::<PyBytes>(py)?.data(py)[0]
139 139 .try_into()
140 140 .map_err(|e: DirstateParseError| {
141 141 PyErr::new::<exc::ValueError, _>(py, e.to_string())
142 142 })?,
143 143 )
144 144 .and_then(|b| Ok(b.to_py_object(py)))
145 145 .or_else(|_| {
146 146 Err(PyErr::new::<exc::OSError, _>(
147 147 py,
148 148 "Dirstate error".to_string(),
149 149 ))
150 150 })
151 151 }
152 152
153 153 def clearambiguoustimes(
154 154 &self,
155 155 files: PyObject,
156 156 now: PyObject
157 157 ) -> PyResult<PyObject> {
158 158 let files: PyResult<Vec<HgPathBuf>> = files
159 159 .iter(py)?
160 160 .map(|filename| {
161 161 Ok(HgPathBuf::from_bytes(
162 162 filename?.extract::<PyBytes>(py)?.data(py),
163 163 ))
164 164 })
165 165 .collect();
166 166 self.inner_shared(py).borrow_mut()?
167 167 .clear_ambiguous_times(files?, now.extract(py)?);
168 168 Ok(py.None())
169 169 }
170 170
171 171 // TODO share the reference
172 172 def nonnormalentries(&self) -> PyResult<PyObject> {
173 173 let (non_normal, other_parent) =
174 174 self.inner_shared(py).borrow().non_normal_other_parent_entries();
175 175
176 176 let locals = PyDict::new(py);
177 177 locals.set_item(
178 178 py,
179 179 "non_normal",
180 180 non_normal
181 181 .iter()
182 182 .map(|v| PyBytes::new(py, v.as_ref()))
183 183 .collect::<Vec<PyBytes>>()
184 184 .to_py_object(py),
185 185 )?;
186 186 locals.set_item(
187 187 py,
188 188 "other_parent",
189 189 other_parent
190 190 .iter()
191 191 .map(|v| PyBytes::new(py, v.as_ref()))
192 192 .collect::<Vec<PyBytes>>()
193 193 .to_py_object(py),
194 194 )?;
195 195
196 196 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
197 197 }
198 198
199 199 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
200 200 let d = d.extract::<PyBytes>(py)?;
201 201 Ok(self.inner_shared(py).borrow_mut()?
202 202 .has_tracked_dir(HgPath::new(d.data(py)))
203 .map_err(|e| {
204 PyErr::new::<exc::ValueError, _>(py, e.to_string())
205 })?
203 206 .to_py_object(py))
204 207 }
205 208
206 209 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
207 210 let d = d.extract::<PyBytes>(py)?;
208 211 Ok(self.inner_shared(py).borrow_mut()?
209 212 .has_dir(HgPath::new(d.data(py)))
213 .map_err(|e| {
214 PyErr::new::<exc::ValueError, _>(py, e.to_string())
215 })?
210 216 .to_py_object(py))
211 217 }
212 218
213 219 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
214 220 self.inner_shared(py).borrow_mut()?
215 221 .parents(st.extract::<PyBytes>(py)?.data(py))
216 222 .and_then(|d| {
217 223 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
218 224 .to_py_object(py))
219 225 })
220 226 .or_else(|_| {
221 227 Err(PyErr::new::<exc::OSError, _>(
222 228 py,
223 229 "Dirstate error".to_string(),
224 230 ))
225 231 })
226 232 }
227 233
228 234 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
229 235 let p1 = extract_node_id(py, &p1)?;
230 236 let p2 = extract_node_id(py, &p2)?;
231 237
232 238 self.inner_shared(py).borrow_mut()?
233 239 .set_parents(&DirstateParents { p1, p2 });
234 240 Ok(py.None())
235 241 }
236 242
237 243 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
238 244 match self.inner_shared(py).borrow_mut()?
239 245 .read(st.extract::<PyBytes>(py)?.data(py))
240 246 {
241 247 Ok(Some(parents)) => Ok(Some(
242 248 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
243 249 .to_py_object(py)
244 250 .into_object(),
245 251 )),
246 252 Ok(None) => Ok(Some(py.None())),
247 253 Err(_) => Err(PyErr::new::<exc::OSError, _>(
248 254 py,
249 255 "Dirstate error".to_string(),
250 256 )),
251 257 }
252 258 }
253 259 def write(
254 260 &self,
255 261 p1: PyObject,
256 262 p2: PyObject,
257 263 now: PyObject
258 264 ) -> PyResult<PyBytes> {
259 265 let now = Duration::new(now.extract(py)?, 0);
260 266 let parents = DirstateParents {
261 267 p1: extract_node_id(py, &p1)?,
262 268 p2: extract_node_id(py, &p2)?,
263 269 };
264 270
265 271 match self.inner_shared(py).borrow_mut()?.pack(parents, now) {
266 272 Ok(packed) => Ok(PyBytes::new(py, &packed)),
267 273 Err(_) => Err(PyErr::new::<exc::OSError, _>(
268 274 py,
269 275 "Dirstate error".to_string(),
270 276 )),
271 277 }
272 278 }
273 279
274 280 def filefoldmapasdict(&self) -> PyResult<PyDict> {
275 281 let dict = PyDict::new(py);
276 282 for (key, value) in
277 283 self.inner_shared(py).borrow_mut()?.build_file_fold_map().iter()
278 284 {
279 285 dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?;
280 286 }
281 287 Ok(dict)
282 288 }
283 289
284 290 def __len__(&self) -> PyResult<usize> {
285 291 Ok(self.inner_shared(py).borrow().len())
286 292 }
287 293
288 294 def __contains__(&self, key: PyObject) -> PyResult<bool> {
289 295 let key = key.extract::<PyBytes>(py)?;
290 296 Ok(self.inner_shared(py).borrow().contains_key(HgPath::new(key.data(py))))
291 297 }
292 298
293 299 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
294 300 let key = key.extract::<PyBytes>(py)?;
295 301 let key = HgPath::new(key.data(py));
296 302 match self.inner_shared(py).borrow().get(key) {
297 303 Some(entry) => {
298 304 Ok(make_dirstate_tuple(py, entry)?)
299 305 },
300 306 None => Err(PyErr::new::<exc::KeyError, _>(
301 307 py,
302 308 String::from_utf8_lossy(key.as_bytes()),
303 309 )),
304 310 }
305 311 }
306 312
307 313 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
308 314 let leaked_ref = self.inner_shared(py).leak_immutable();
309 315 DirstateMapKeysIterator::from_inner(
310 316 py,
311 317 unsafe { leaked_ref.map(py, |o| o.iter()) },
312 318 )
313 319 }
314 320
315 321 def items(&self) -> PyResult<DirstateMapItemsIterator> {
316 322 let leaked_ref = self.inner_shared(py).leak_immutable();
317 323 DirstateMapItemsIterator::from_inner(
318 324 py,
319 325 unsafe { leaked_ref.map(py, |o| o.iter()) },
320 326 )
321 327 }
322 328
323 329 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
324 330 let leaked_ref = self.inner_shared(py).leak_immutable();
325 331 DirstateMapKeysIterator::from_inner(
326 332 py,
327 333 unsafe { leaked_ref.map(py, |o| o.iter()) },
328 334 )
329 335 }
330 336
331 337 def getdirs(&self) -> PyResult<Dirs> {
332 338 // TODO don't copy, share the reference
333 self.inner_shared(py).borrow_mut()?.set_dirs();
339 self.inner_shared(py).borrow_mut()?.set_dirs()
340 .map_err(|e| {
341 PyErr::new::<exc::ValueError, _>(py, e.to_string())
342 })?;
334 343 Dirs::from_inner(
335 344 py,
336 345 DirsMultiset::from_dirstate(
337 346 &self.inner_shared(py).borrow(),
338 347 Some(EntryState::Removed),
339 ),
348 )
349 .map_err(|e| {
350 PyErr::new::<exc::ValueError, _>(py, e.to_string())
351 })?,
340 352 )
341 353 }
342 354 def getalldirs(&self) -> PyResult<Dirs> {
343 355 // TODO don't copy, share the reference
344 self.inner_shared(py).borrow_mut()?.set_all_dirs();
356 self.inner_shared(py).borrow_mut()?.set_all_dirs()
357 .map_err(|e| {
358 PyErr::new::<exc::ValueError, _>(py, e.to_string())
359 })?;
345 360 Dirs::from_inner(
346 361 py,
347 362 DirsMultiset::from_dirstate(
348 363 &self.inner_shared(py).borrow(),
349 364 None,
350 ),
365 ).map_err(|e| {
366 PyErr::new::<exc::ValueError, _>(py, e.to_string())
367 })?,
351 368 )
352 369 }
353 370
354 371 // TODO all copymap* methods, see docstring above
355 372 def copymapcopy(&self) -> PyResult<PyDict> {
356 373 let dict = PyDict::new(py);
357 374 for (key, value) in self.inner_shared(py).borrow().copy_map.iter() {
358 375 dict.set_item(
359 376 py,
360 377 PyBytes::new(py, key.as_ref()),
361 378 PyBytes::new(py, value.as_ref()),
362 379 )?;
363 380 }
364 381 Ok(dict)
365 382 }
366 383
367 384 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
368 385 let key = key.extract::<PyBytes>(py)?;
369 386 match self.inner_shared(py).borrow().copy_map.get(HgPath::new(key.data(py))) {
370 387 Some(copy) => Ok(PyBytes::new(py, copy.as_ref())),
371 388 None => Err(PyErr::new::<exc::KeyError, _>(
372 389 py,
373 390 String::from_utf8_lossy(key.data(py)),
374 391 )),
375 392 }
376 393 }
377 394 def copymap(&self) -> PyResult<CopyMap> {
378 395 CopyMap::from_inner(py, self.clone_ref(py))
379 396 }
380 397
381 398 def copymaplen(&self) -> PyResult<usize> {
382 399 Ok(self.inner_shared(py).borrow().copy_map.len())
383 400 }
384 401 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
385 402 let key = key.extract::<PyBytes>(py)?;
386 403 Ok(self
387 404 .inner_shared(py)
388 405 .borrow()
389 406 .copy_map
390 407 .contains_key(HgPath::new(key.data(py))))
391 408 }
392 409 def copymapget(
393 410 &self,
394 411 key: PyObject,
395 412 default: Option<PyObject>
396 413 ) -> PyResult<Option<PyObject>> {
397 414 let key = key.extract::<PyBytes>(py)?;
398 415 match self
399 416 .inner_shared(py)
400 417 .borrow()
401 418 .copy_map
402 419 .get(HgPath::new(key.data(py)))
403 420 {
404 421 Some(copy) => Ok(Some(
405 422 PyBytes::new(py, copy.as_ref()).into_object(),
406 423 )),
407 424 None => Ok(default),
408 425 }
409 426 }
410 427 def copymapsetitem(
411 428 &self,
412 429 key: PyObject,
413 430 value: PyObject
414 431 ) -> PyResult<PyObject> {
415 432 let key = key.extract::<PyBytes>(py)?;
416 433 let value = value.extract::<PyBytes>(py)?;
417 434 self.inner_shared(py).borrow_mut()?.copy_map.insert(
418 435 HgPathBuf::from_bytes(key.data(py)),
419 436 HgPathBuf::from_bytes(value.data(py)),
420 437 );
421 438 Ok(py.None())
422 439 }
423 440 def copymappop(
424 441 &self,
425 442 key: PyObject,
426 443 default: Option<PyObject>
427 444 ) -> PyResult<Option<PyObject>> {
428 445 let key = key.extract::<PyBytes>(py)?;
429 446 match self
430 447 .inner_shared(py)
431 448 .borrow_mut()?
432 449 .copy_map
433 450 .remove(HgPath::new(key.data(py)))
434 451 {
435 452 Some(_) => Ok(None),
436 453 None => Ok(default),
437 454 }
438 455 }
439 456
440 457 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
441 458 let leaked_ref = self.inner_shared(py).leak_immutable();
442 459 CopyMapKeysIterator::from_inner(
443 460 py,
444 461 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
445 462 )
446 463 }
447 464
448 465 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
449 466 let leaked_ref = self.inner_shared(py).leak_immutable();
450 467 CopyMapItemsIterator::from_inner(
451 468 py,
452 469 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
453 470 )
454 471 }
455 472
456 473 });
457 474
458 475 impl DirstateMap {
459 476 pub fn get_inner<'a>(
460 477 &'a self,
461 478 py: Python<'a>,
462 479 ) -> Ref<'a, RustDirstateMap> {
463 480 self.inner_shared(py).borrow()
464 481 }
465 482 fn translate_key(
466 483 py: Python,
467 484 res: (&HgPathBuf, &DirstateEntry),
468 485 ) -> PyResult<Option<PyBytes>> {
469 486 Ok(Some(PyBytes::new(py, res.0.as_ref())))
470 487 }
471 488 fn translate_key_value(
472 489 py: Python,
473 490 res: (&HgPathBuf, &DirstateEntry),
474 491 ) -> PyResult<Option<(PyBytes, PyObject)>> {
475 492 let (f, entry) = res;
476 493 Ok(Some((
477 494 PyBytes::new(py, f.as_ref()),
478 495 make_dirstate_tuple(py, entry)?,
479 496 )))
480 497 }
481 498 }
482 499
483 500 py_shared_ref!(DirstateMap, RustDirstateMap, inner, inner_shared);
484 501
485 502 py_shared_iterator!(
486 503 DirstateMapKeysIterator,
487 504 PyLeaked<StateMapIter<'static>>,
488 505 DirstateMap::translate_key,
489 506 Option<PyBytes>
490 507 );
491 508
492 509 py_shared_iterator!(
493 510 DirstateMapItemsIterator,
494 511 PyLeaked<StateMapIter<'static>>,
495 512 DirstateMap::translate_key_value,
496 513 Option<(PyBytes, PyObject)>
497 514 );
498 515
499 516 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
500 517 let bytes = obj.extract::<PyBytes>(py)?;
501 518 match bytes.data(py).try_into() {
502 519 Ok(s) => Ok(s),
503 520 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
504 521 }
505 522 }
General Comments 0
You need to be logged in to leave comments. Login now