##// END OF EJS Templates
rust-dirs: address failing tests for `dirs` impl with a temporary fix...
Raphaël Gomès -
r44612:1fe2e574 default
parent child Browse files
Show More
@@ -1,329 +1,335 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 };
15 15 use std::collections::hash_map::{self, Entry};
16 16 use std::collections::HashMap;
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: HashMap<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 vec: &HashMap<HgPathBuf, DirstateEntry>,
32 32 skip_state: Option<EntryState>,
33 33 ) -> Self {
34 34 let mut multiset = DirsMultiset {
35 35 inner: HashMap::new(),
36 36 };
37 37
38 38 for (filename, DirstateEntry { state, .. }) in vec {
39 39 // This `if` is optimized out of the loop
40 40 if let Some(skip) = skip_state {
41 41 if skip != *state {
42 42 multiset.add_path(filename);
43 43 }
44 44 } else {
45 45 multiset.add_path(filename);
46 46 }
47 47 }
48 48
49 49 multiset
50 50 }
51 51
52 52 /// Initializes the multiset from a manifest.
53 53 pub fn from_manifest(vec: &Vec<HgPathBuf>) -> Self {
54 54 let mut multiset = DirsMultiset {
55 55 inner: HashMap::new(),
56 56 };
57 57
58 58 for filename in vec {
59 59 multiset.add_path(filename);
60 60 }
61 61
62 62 multiset
63 63 }
64 64
65 65 /// Increases the count of deepest directory contained in the path.
66 66 ///
67 67 /// If the directory is not yet in the map, adds its parents.
68 pub fn add_path(&mut self, path: &HgPath) {
68 pub fn add_path(&mut self, path: &HgPath) -> Result<(), DirstateMapError> {
69 69 for subpath in files::find_dirs(path) {
70 if subpath.as_bytes().last() == Some(&b'/') {
71 // TODO Remove this once PathAuditor is certified
72 // as the only entrypoint for path data
73 return Err(DirstateMapError::ConsecutiveSlashes);
74 }
70 75 if let Some(val) = self.inner.get_mut(subpath) {
71 76 *val += 1;
72 77 break;
73 78 }
74 79 self.inner.insert(subpath.to_owned(), 1);
75 80 }
81 Ok(())
76 82 }
77 83
78 84 /// Decreases the count of deepest directory contained in the path.
79 85 ///
80 86 /// If it is the only reference, decreases all parents until one is
81 87 /// removed.
82 88 /// If the directory is not in the map, something horrible has happened.
83 89 pub fn delete_path(
84 90 &mut self,
85 91 path: &HgPath,
86 92 ) -> Result<(), DirstateMapError> {
87 93 for subpath in files::find_dirs(path) {
88 94 match self.inner.entry(subpath.to_owned()) {
89 95 Entry::Occupied(mut entry) => {
90 96 let val = entry.get().clone();
91 97 if val > 1 {
92 98 entry.insert(val - 1);
93 99 break;
94 100 }
95 101 entry.remove();
96 102 }
97 103 Entry::Vacant(_) => {
98 104 return Err(DirstateMapError::PathNotFound(
99 105 path.to_owned(),
100 106 ))
101 107 }
102 108 };
103 109 }
104 110
105 111 Ok(())
106 112 }
107 113
108 114 pub fn contains(&self, key: &HgPath) -> bool {
109 115 self.inner.contains_key(key)
110 116 }
111 117
112 118 pub fn iter(&self) -> DirsMultisetIter {
113 119 self.inner.keys()
114 120 }
115 121
116 122 pub fn len(&self) -> usize {
117 123 self.inner.len()
118 124 }
119 125 }
120 126
121 127 #[cfg(test)]
122 128 mod tests {
123 129 use super::*;
124 130 use std::collections::HashMap;
125 131
126 132 #[test]
127 133 fn test_delete_path_path_not_found() {
128 134 let mut map = DirsMultiset::from_manifest(&vec![]);
129 135 let path = HgPathBuf::from_bytes(b"doesnotexist/");
130 136 assert_eq!(
131 137 Err(DirstateMapError::PathNotFound(path.to_owned())),
132 138 map.delete_path(&path)
133 139 );
134 140 }
135 141
136 142 #[test]
137 143 fn test_delete_path_empty_path() {
138 144 let mut map = DirsMultiset::from_manifest(&vec![HgPathBuf::new()]);
139 145 let path = HgPath::new(b"");
140 146 assert_eq!(Ok(()), map.delete_path(path));
141 147 assert_eq!(
142 148 Err(DirstateMapError::PathNotFound(path.to_owned())),
143 149 map.delete_path(path)
144 150 );
145 151 }
146 152
147 153 #[test]
148 154 fn test_delete_path_successful() {
149 155 let mut map = DirsMultiset {
150 156 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
151 157 .iter()
152 158 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
153 159 .collect(),
154 160 };
155 161
156 162 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
157 163 eprintln!("{:?}", map);
158 164 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
159 165 eprintln!("{:?}", map);
160 166 assert_eq!(
161 167 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
162 168 b"a/b/"
163 169 ))),
164 170 map.delete_path(HgPath::new(b"a/b/"))
165 171 );
166 172
167 173 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
168 174 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
169 175 eprintln!("{:?}", map);
170 176 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/")));
171 177 eprintln!("{:?}", map);
172 178
173 179 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/")));
174 180 assert_eq!(
175 181 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
176 182 b"a/c/"
177 183 ))),
178 184 map.delete_path(HgPath::new(b"a/c/"))
179 185 );
180 186 }
181 187
182 188 #[test]
183 189 fn test_add_path_empty_path() {
184 190 let mut map = DirsMultiset::from_manifest(&vec![]);
185 191 let path = HgPath::new(b"");
186 192 map.add_path(path);
187 193
188 194 assert_eq!(1, map.len());
189 195 }
190 196
191 197 #[test]
192 198 fn test_add_path_successful() {
193 199 let mut map = DirsMultiset::from_manifest(&vec![]);
194 200
195 201 map.add_path(HgPath::new(b"a/"));
196 202 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
197 203 assert_eq!(1, *map.inner.get(HgPath::new(b"")).unwrap());
198 204 assert_eq!(2, map.len());
199 205
200 206 // Non directory should be ignored
201 207 map.add_path(HgPath::new(b"a"));
202 208 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
203 209 assert_eq!(2, map.len());
204 210
205 211 // Non directory will still add its base
206 212 map.add_path(HgPath::new(b"a/b"));
207 213 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
208 214 assert_eq!(2, map.len());
209 215
210 216 // Duplicate path works
211 217 map.add_path(HgPath::new(b"a/"));
212 218 assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap());
213 219
214 220 // Nested dir adds to its base
215 221 map.add_path(HgPath::new(b"a/b/"));
216 222 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
217 223 assert_eq!(1, *map.inner.get(HgPath::new(b"a/b")).unwrap());
218 224
219 225 // but not its base's base, because it already existed
220 226 map.add_path(HgPath::new(b"a/b/c/"));
221 227 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
222 228 assert_eq!(2, *map.inner.get(HgPath::new(b"a/b")).unwrap());
223 229
224 230 map.add_path(HgPath::new(b"a/c/"));
225 231 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
226 232
227 233 let expected = DirsMultiset {
228 234 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
229 235 .iter()
230 236 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
231 237 .collect(),
232 238 };
233 239 assert_eq!(map, expected);
234 240 }
235 241
236 242 #[test]
237 243 fn test_dirsmultiset_new_empty() {
238 244 let new = DirsMultiset::from_manifest(&vec![]);
239 245 let expected = DirsMultiset {
240 246 inner: HashMap::new(),
241 247 };
242 248 assert_eq!(expected, new);
243 249
244 250 let new = DirsMultiset::from_dirstate(&HashMap::new(), None);
245 251 let expected = DirsMultiset {
246 252 inner: HashMap::new(),
247 253 };
248 254 assert_eq!(expected, new);
249 255 }
250 256
251 257 #[test]
252 258 fn test_dirsmultiset_new_no_skip() {
253 259 let input_vec = ["a/", "b/", "a/c", "a/d/"]
254 260 .iter()
255 261 .map(|e| HgPathBuf::from_bytes(e.as_bytes()))
256 262 .collect();
257 263 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
258 264 .iter()
259 265 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
260 266 .collect();
261 267
262 268 let new = DirsMultiset::from_manifest(&input_vec);
263 269 let expected = DirsMultiset {
264 270 inner: expected_inner,
265 271 };
266 272 assert_eq!(expected, new);
267 273
268 274 let input_map = ["a/", "b/", "a/c", "a/d/"]
269 275 .iter()
270 276 .map(|f| {
271 277 (
272 278 HgPathBuf::from_bytes(f.as_bytes()),
273 279 DirstateEntry {
274 280 state: EntryState::Normal,
275 281 mode: 0,
276 282 mtime: 0,
277 283 size: 0,
278 284 },
279 285 )
280 286 })
281 287 .collect();
282 288 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
283 289 .iter()
284 290 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
285 291 .collect();
286 292
287 293 let new = DirsMultiset::from_dirstate(&input_map, None);
288 294 let expected = DirsMultiset {
289 295 inner: expected_inner,
290 296 };
291 297 assert_eq!(expected, new);
292 298 }
293 299
294 300 #[test]
295 301 fn test_dirsmultiset_new_skip() {
296 302 let input_map = [
297 303 ("a/", EntryState::Normal),
298 304 ("a/b/", EntryState::Normal),
299 305 ("a/c", EntryState::Removed),
300 306 ("a/d/", EntryState::Merged),
301 307 ]
302 308 .iter()
303 309 .map(|(f, state)| {
304 310 (
305 311 HgPathBuf::from_bytes(f.as_bytes()),
306 312 DirstateEntry {
307 313 state: *state,
308 314 mode: 0,
309 315 mtime: 0,
310 316 size: 0,
311 317 },
312 318 )
313 319 })
314 320 .collect();
315 321
316 322 // "a" incremented with "a/c" and "a/d/"
317 323 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
318 324 .iter()
319 325 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
320 326 .collect();
321 327
322 328 let new =
323 329 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal));
324 330 let expected = DirsMultiset {
325 331 inner: expected_inner,
326 332 };
327 333 assert_eq!(expected, new);
328 334 }
329 335 }
@@ -1,426 +1,427 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, StateMap,
17 17 };
18 18 use core::borrow::Borrow;
19 19 use std::collections::{HashMap, 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 = HashMap<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 dirs.add_path(filename)
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 all_dirs.add_path(filename)
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 Ok(())
107 108 }
108 109
109 110 /// Mark a file as removed in the dirstate.
110 111 ///
111 112 /// The `size` parameter is used to store sentinel values that indicate
112 113 /// the file's previous state. In the future, we should refactor this
113 114 /// to be more explicit about what that state is.
114 115 pub fn remove_file(
115 116 &mut self,
116 117 filename: &HgPath,
117 118 old_state: EntryState,
118 119 size: i32,
119 120 ) -> Result<(), DirstateMapError> {
120 121 if old_state != EntryState::Unknown && old_state != EntryState::Removed
121 122 {
122 123 if let Some(ref mut dirs) = self.dirs {
123 124 dirs.delete_path(filename)?;
124 125 }
125 126 }
126 127 if old_state == EntryState::Unknown {
127 128 if let Some(ref mut all_dirs) = self.all_dirs {
128 129 all_dirs.add_path(filename);
129 130 }
130 131 }
131 132
132 133 if let Some(ref mut file_fold_map) = self.file_fold_map {
133 134 file_fold_map.remove(&normalize_case(filename));
134 135 }
135 136 self.state_map.insert(
136 137 filename.to_owned(),
137 138 DirstateEntry {
138 139 state: EntryState::Removed,
139 140 mode: 0,
140 141 size,
141 142 mtime: 0,
142 143 },
143 144 );
144 145 self.non_normal_set.insert(filename.to_owned());
145 146 Ok(())
146 147 }
147 148
148 149 /// Remove a file from the dirstate.
149 150 /// Returns `true` if the file was previously recorded.
150 151 pub fn drop_file(
151 152 &mut self,
152 153 filename: &HgPath,
153 154 old_state: EntryState,
154 155 ) -> Result<bool, DirstateMapError> {
155 156 let exists = self.state_map.remove(filename).is_some();
156 157
157 158 if exists {
158 159 if old_state != EntryState::Removed {
159 160 if let Some(ref mut dirs) = self.dirs {
160 161 dirs.delete_path(filename)?;
161 162 }
162 163 }
163 164 if let Some(ref mut all_dirs) = self.all_dirs {
164 165 all_dirs.delete_path(filename)?;
165 166 }
166 167 }
167 168 if let Some(ref mut file_fold_map) = self.file_fold_map {
168 169 file_fold_map.remove(&normalize_case(filename));
169 170 }
170 171 self.non_normal_set.remove(filename);
171 172
172 173 Ok(exists)
173 174 }
174 175
175 176 pub fn clear_ambiguous_times(
176 177 &mut self,
177 178 filenames: Vec<HgPathBuf>,
178 179 now: i32,
179 180 ) {
180 181 for filename in filenames {
181 182 let mut changed = false;
182 183 self.state_map
183 184 .entry(filename.to_owned())
184 185 .and_modify(|entry| {
185 186 if entry.state == EntryState::Normal && entry.mtime == now
186 187 {
187 188 changed = true;
188 189 *entry = DirstateEntry {
189 190 mtime: MTIME_UNSET,
190 191 ..*entry
191 192 };
192 193 }
193 194 });
194 195 if changed {
195 196 self.non_normal_set.insert(filename.to_owned());
196 197 }
197 198 }
198 199 }
199 200
200 201 pub fn non_normal_other_parent_entries(
201 202 &self,
202 203 ) -> (HashSet<HgPathBuf>, HashSet<HgPathBuf>) {
203 204 let mut non_normal = HashSet::new();
204 205 let mut other_parent = HashSet::new();
205 206
206 207 for (
207 208 filename,
208 209 DirstateEntry {
209 210 state, size, mtime, ..
210 211 },
211 212 ) in self.state_map.iter()
212 213 {
213 214 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
214 215 non_normal.insert(filename.to_owned());
215 216 }
216 217 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
217 218 {
218 219 other_parent.insert(filename.to_owned());
219 220 }
220 221 }
221 222
222 223 (non_normal, other_parent)
223 224 }
224 225
225 226 /// Both of these setters and their uses appear to be the simplest way to
226 227 /// emulate a Python lazy property, but it is ugly and unidiomatic.
227 228 /// TODO One day, rewriting this struct using the typestate might be a
228 229 /// good idea.
229 230 pub fn set_all_dirs(&mut self) {
230 231 if self.all_dirs.is_none() {
231 232 self.all_dirs =
232 233 Some(DirsMultiset::from_dirstate(&self.state_map, None));
233 234 }
234 235 }
235 236
236 237 pub fn set_dirs(&mut self) {
237 238 if self.dirs.is_none() {
238 239 self.dirs = Some(DirsMultiset::from_dirstate(
239 240 &self.state_map,
240 241 Some(EntryState::Removed),
241 242 ));
242 243 }
243 244 }
244 245
245 246 pub fn has_tracked_dir(&mut self, directory: &HgPath) -> bool {
246 247 self.set_dirs();
247 248 self.dirs.as_ref().unwrap().contains(directory)
248 249 }
249 250
250 251 pub fn has_dir(&mut self, directory: &HgPath) -> bool {
251 252 self.set_all_dirs();
252 253 self.all_dirs.as_ref().unwrap().contains(directory)
253 254 }
254 255
255 256 pub fn parents(
256 257 &mut self,
257 258 file_contents: &[u8],
258 259 ) -> Result<&DirstateParents, DirstateError> {
259 260 if let Some(ref parents) = self.parents {
260 261 return Ok(parents);
261 262 }
262 263 let parents;
263 264 if file_contents.len() == PARENT_SIZE * 2 {
264 265 parents = DirstateParents {
265 266 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
266 267 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
267 268 .try_into()
268 269 .unwrap(),
269 270 };
270 271 } else if file_contents.is_empty() {
271 272 parents = DirstateParents {
272 273 p1: NULL_ID,
273 274 p2: NULL_ID,
274 275 };
275 276 } else {
276 277 return Err(DirstateError::Parse(DirstateParseError::Damaged));
277 278 }
278 279
279 280 self.parents = Some(parents);
280 281 Ok(self.parents.as_ref().unwrap())
281 282 }
282 283
283 284 pub fn set_parents(&mut self, parents: &DirstateParents) {
284 285 self.parents = Some(parents.clone());
285 286 self.dirty_parents = true;
286 287 }
287 288
288 289 pub fn read(
289 290 &mut self,
290 291 file_contents: &[u8],
291 292 ) -> Result<Option<DirstateParents>, DirstateError> {
292 293 if file_contents.is_empty() {
293 294 return Ok(None);
294 295 }
295 296
296 297 let parents = parse_dirstate(
297 298 &mut self.state_map,
298 299 &mut self.copy_map,
299 300 file_contents,
300 301 )?;
301 302
302 303 if !self.dirty_parents {
303 304 self.set_parents(&parents);
304 305 }
305 306
306 307 Ok(Some(parents))
307 308 }
308 309
309 310 pub fn pack(
310 311 &mut self,
311 312 parents: DirstateParents,
312 313 now: Duration,
313 314 ) -> Result<Vec<u8>, DirstateError> {
314 315 let packed =
315 316 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
316 317
317 318 self.dirty_parents = false;
318 319
319 320 let result = self.non_normal_other_parent_entries();
320 321 self.non_normal_set = result.0;
321 322 self.other_parent_set = result.1;
322 323 Ok(packed)
323 324 }
324 325
325 326 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
326 327 if let Some(ref file_fold_map) = self.file_fold_map {
327 328 return file_fold_map;
328 329 }
329 330 let mut new_file_fold_map = FileFoldMap::new();
330 331 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
331 332 {
332 333 if *state == EntryState::Removed {
333 334 new_file_fold_map
334 335 .insert(normalize_case(filename), filename.to_owned());
335 336 }
336 337 }
337 338 self.file_fold_map = Some(new_file_fold_map);
338 339 self.file_fold_map.as_ref().unwrap()
339 340 }
340 341 }
341 342
342 343 #[cfg(test)]
343 344 mod tests {
344 345 use super::*;
345 346
346 347 #[test]
347 348 fn test_dirs_multiset() {
348 349 let mut map = DirstateMap::new();
349 350 assert!(map.dirs.is_none());
350 351 assert!(map.all_dirs.is_none());
351 352
352 353 assert_eq!(false, map.has_dir(HgPath::new(b"nope")));
353 354 assert!(map.all_dirs.is_some());
354 355 assert!(map.dirs.is_none());
355 356
356 357 assert_eq!(false, map.has_tracked_dir(HgPath::new(b"nope")));
357 358 assert!(map.dirs.is_some());
358 359 }
359 360
360 361 #[test]
361 362 fn test_add_file() {
362 363 let mut map = DirstateMap::new();
363 364
364 365 assert_eq!(0, map.len());
365 366
366 367 map.add_file(
367 368 HgPath::new(b"meh"),
368 369 EntryState::Normal,
369 370 DirstateEntry {
370 371 state: EntryState::Normal,
371 372 mode: 1337,
372 373 mtime: 1337,
373 374 size: 1337,
374 375 },
375 376 );
376 377
377 378 assert_eq!(1, map.len());
378 379 assert_eq!(0, map.non_normal_set.len());
379 380 assert_eq!(0, map.other_parent_set.len());
380 381 }
381 382
382 383 #[test]
383 384 fn test_non_normal_other_parent_entries() {
384 385 let map: DirstateMap = [
385 386 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
386 387 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
387 388 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
388 389 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
389 390 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
390 391 (b"f6", (EntryState::Added, 1337, 1337, -1)),
391 392 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
392 393 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
393 394 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
394 395 (b"fa", (EntryState::Added, 1337, -2, 1337)),
395 396 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
396 397 ]
397 398 .iter()
398 399 .map(|(fname, (state, mode, size, mtime))| {
399 400 (
400 401 HgPathBuf::from_bytes(fname.as_ref()),
401 402 DirstateEntry {
402 403 state: *state,
403 404 mode: *mode,
404 405 size: *size,
405 406 mtime: *mtime,
406 407 },
407 408 )
408 409 })
409 410 .collect();
410 411
411 412 let non_normal = [
412 413 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
413 414 ]
414 415 .iter()
415 416 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
416 417 .collect();
417 418
418 419 let mut other_parent = HashSet::new();
419 420 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
420 421
421 422 assert_eq!(
422 423 (non_normal, other_parent),
423 424 map.non_normal_other_parent_entries()
424 425 );
425 426 }
426 427 }
@@ -1,152 +1,166 b''
1 1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5 mod ancestors;
6 6 pub mod dagops;
7 7 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
8 8 mod dirstate;
9 9 pub mod discovery;
10 10 pub mod testing; // unconditionally built, for use from integration tests
11 11 pub use dirstate::{
12 12 dirs_multiset::{DirsMultiset, DirsMultisetIter},
13 13 dirstate_map::DirstateMap,
14 14 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
15 15 status::status,
16 16 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
17 17 StateMap, StateMapIter,
18 18 };
19 19 mod filepatterns;
20 20 pub mod matchers;
21 21 pub mod utils;
22 22
23 23 use crate::utils::hg_path::HgPathBuf;
24 24 pub use filepatterns::{
25 25 build_single_regex, read_pattern_file, PatternSyntax, PatternTuple,
26 26 };
27 27
28 28 /// Mercurial revision numbers
29 29 ///
30 30 /// As noted in revlog.c, revision numbers are actually encoded in
31 31 /// 4 bytes, and are liberally converted to ints, whence the i32
32 32 pub type Revision = i32;
33 33
34 34 /// Marker expressing the absence of a parent
35 35 ///
36 36 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
37 37 /// to be smaller that all existing revisions.
38 38 pub const NULL_REVISION: Revision = -1;
39 39
40 40 /// Same as `mercurial.node.wdirrev`
41 41 ///
42 42 /// This is also equal to `i32::max_value()`, but it's better to spell
43 43 /// it out explicitely, same as in `mercurial.node`
44 44 pub const WORKING_DIRECTORY_REVISION: Revision = 0x7fffffff;
45 45
46 46 /// The simplest expression of what we need of Mercurial DAGs.
47 47 pub trait Graph {
48 48 /// Return the two parents of the given `Revision`.
49 49 ///
50 50 /// Each of the parents can be independently `NULL_REVISION`
51 51 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError>;
52 52 }
53 53
54 54 pub type LineNumber = usize;
55 55
56 56 #[derive(Clone, Debug, PartialEq)]
57 57 pub enum GraphError {
58 58 ParentOutOfRange(Revision),
59 59 WorkingDirectoryUnsupported,
60 60 }
61 61
62 62 #[derive(Clone, Debug, PartialEq)]
63 63 pub enum DirstateParseError {
64 64 TooLittleData,
65 65 Overflow,
66 66 CorruptedEntry(String),
67 67 Damaged,
68 68 }
69 69
70 70 impl From<std::io::Error> for DirstateParseError {
71 71 fn from(e: std::io::Error) -> Self {
72 72 DirstateParseError::CorruptedEntry(e.to_string())
73 73 }
74 74 }
75 75
76 76 impl ToString for DirstateParseError {
77 77 fn to_string(&self) -> String {
78 78 use crate::DirstateParseError::*;
79 79 match self {
80 80 TooLittleData => "Too little data for dirstate.".to_string(),
81 81 Overflow => "Overflow in dirstate.".to_string(),
82 82 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
83 83 Damaged => "Dirstate appears to be damaged.".to_string(),
84 84 }
85 85 }
86 86 }
87 87
88 88 #[derive(Debug, PartialEq)]
89 89 pub enum DirstatePackError {
90 90 CorruptedEntry(String),
91 91 CorruptedParent,
92 92 BadSize(usize, usize),
93 93 }
94 94
95 95 impl From<std::io::Error> for DirstatePackError {
96 96 fn from(e: std::io::Error) -> Self {
97 97 DirstatePackError::CorruptedEntry(e.to_string())
98 98 }
99 99 }
100 100 #[derive(Debug, PartialEq)]
101 101 pub enum DirstateMapError {
102 102 PathNotFound(HgPathBuf),
103 103 EmptyPath,
104 ConsecutiveSlashes,
105 }
106
107 impl ToString for DirstateMapError {
108 fn to_string(&self) -> String {
109 use crate::DirstateMapError::*;
110 match self {
111 PathNotFound(_) => "expected a value, found none".to_string(),
112 EmptyPath => "Overflow in dirstate.".to_string(),
113 ConsecutiveSlashes => {
114 "found invalid consecutive slashes in path".to_string()
115 }
116 }
117 }
104 118 }
105 119
106 120 pub enum DirstateError {
107 121 Parse(DirstateParseError),
108 122 Pack(DirstatePackError),
109 123 Map(DirstateMapError),
110 124 IO(std::io::Error),
111 125 }
112 126
113 127 impl From<DirstateParseError> for DirstateError {
114 128 fn from(e: DirstateParseError) -> Self {
115 129 DirstateError::Parse(e)
116 130 }
117 131 }
118 132
119 133 impl From<DirstatePackError> for DirstateError {
120 134 fn from(e: DirstatePackError) -> Self {
121 135 DirstateError::Pack(e)
122 136 }
123 137 }
124 138
125 139 #[derive(Debug)]
126 140 pub enum PatternError {
127 141 UnsupportedSyntax(String),
128 142 }
129 143
130 144 #[derive(Debug)]
131 145 pub enum PatternFileError {
132 146 IO(std::io::Error),
133 147 Pattern(PatternError, LineNumber),
134 148 }
135 149
136 150 impl From<std::io::Error> for PatternFileError {
137 151 fn from(e: std::io::Error) -> Self {
138 152 PatternFileError::IO(e)
139 153 }
140 154 }
141 155
142 156 impl From<DirstateMapError> for DirstateError {
143 157 fn from(e: DirstateMapError) -> Self {
144 158 DirstateError::Map(e)
145 159 }
146 160 }
147 161
148 162 impl From<std::io::Error> for DirstateError {
149 163 fn from(e: std::io::Error) -> Self {
150 164 DirstateError::IO(e)
151 165 }
152 166 }
@@ -1,129 +1,140 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 50 } else {
51 51 let map: Result<Vec<HgPathBuf>, PyErr> = map
52 52 .iter(py)?
53 53 .map(|o| {
54 54 Ok(HgPathBuf::from_bytes(
55 55 o?.extract::<PyBytes>(py)?.data(py),
56 56 ))
57 57 })
58 58 .collect();
59 59 DirsMultiset::from_manifest(&map?)
60 60 };
61 61
62 62 Self::create_instance(
63 63 py,
64 64 PySharedRefCell::new(inner),
65 65 )
66 66 }
67 67
68 68 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
69 69 self.inner_shared(py).borrow_mut()?.add_path(
70 70 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
71 );
72 Ok(py.None())
71 ).and(Ok(py.None())).or_else(|e| {
72 match e {
73 DirstateMapError::EmptyPath => {
74 Ok(py.None())
75 },
76 e => {
77 Err(PyErr::new::<exc::ValueError, _>(
78 py,
79 e.to_string(),
80 ))
81 }
82 }
83 })
73 84 }
74 85
75 86 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
76 87 self.inner_shared(py).borrow_mut()?.delete_path(
77 88 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
78 89 )
79 90 .and(Ok(py.None()))
80 91 .or_else(|e| {
81 92 match e {
82 DirstateMapError::PathNotFound(_p) => {
93 DirstateMapError::EmptyPath => {
94 Ok(py.None())
95 },
96 e => {
83 97 Err(PyErr::new::<exc::ValueError, _>(
84 98 py,
85 "expected a value, found none".to_string(),
99 e.to_string(),
86 100 ))
87 101 }
88 DirstateMapError::EmptyPath => {
89 Ok(py.None())
90 }
91 102 }
92 103 })
93 104 }
94 105 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
95 106 let leaked_ref = self.inner_shared(py).leak_immutable();
96 107 DirsMultisetKeysIterator::from_inner(
97 108 py,
98 109 unsafe { leaked_ref.map(py, |o| o.iter()) },
99 110 )
100 111 }
101 112
102 113 def __contains__(&self, item: PyObject) -> PyResult<bool> {
103 114 Ok(self.inner_shared(py).borrow().contains(HgPath::new(
104 115 item.extract::<PyBytes>(py)?.data(py).as_ref(),
105 116 )))
106 117 }
107 118 });
108 119
109 120 py_shared_ref!(Dirs, DirsMultiset, inner, inner_shared);
110 121
111 122 impl Dirs {
112 123 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
113 124 Self::create_instance(py, PySharedRefCell::new(d))
114 125 }
115 126
116 127 fn translate_key(
117 128 py: Python,
118 129 res: &HgPathBuf,
119 130 ) -> PyResult<Option<PyBytes>> {
120 131 Ok(Some(PyBytes::new(py, res.as_ref())))
121 132 }
122 133 }
123 134
124 135 py_shared_iterator!(
125 136 DirsMultisetKeysIterator,
126 137 PyLeaked<DirsMultisetIter<'static>>,
127 138 Dirs::translate_key,
128 139 Option<PyBytes>
129 140 );
@@ -1,504 +1,505 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 DirstateParents, DirstateParseError, EntryState, StateMapIter,
29 PARENT_SIZE,
28 DirstateMapError, DirstateParents, DirstateParseError, EntryState,
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 );
101 Ok(py.None())
100 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
101 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
102 })
102 103 }
103 104
104 105 def removefile(
105 106 &self,
106 107 f: PyObject,
107 108 oldstate: PyObject,
108 109 size: PyObject
109 110 ) -> PyResult<PyObject> {
110 111 self.inner_shared(py).borrow_mut()?
111 112 .remove_file(
112 113 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
113 114 oldstate.extract::<PyBytes>(py)?.data(py)[0]
114 115 .try_into()
115 116 .map_err(|e: DirstateParseError| {
116 117 PyErr::new::<exc::ValueError, _>(py, e.to_string())
117 118 })?,
118 119 size.extract(py)?,
119 120 )
120 121 .or_else(|_| {
121 122 Err(PyErr::new::<exc::OSError, _>(
122 123 py,
123 124 "Dirstate error".to_string(),
124 125 ))
125 126 })?;
126 127 Ok(py.None())
127 128 }
128 129
129 130 def dropfile(
130 131 &self,
131 132 f: PyObject,
132 133 oldstate: PyObject
133 134 ) -> PyResult<PyBool> {
134 135 self.inner_shared(py).borrow_mut()?
135 136 .drop_file(
136 137 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
137 138 oldstate.extract::<PyBytes>(py)?.data(py)[0]
138 139 .try_into()
139 140 .map_err(|e: DirstateParseError| {
140 141 PyErr::new::<exc::ValueError, _>(py, e.to_string())
141 142 })?,
142 143 )
143 144 .and_then(|b| Ok(b.to_py_object(py)))
144 145 .or_else(|_| {
145 146 Err(PyErr::new::<exc::OSError, _>(
146 147 py,
147 148 "Dirstate error".to_string(),
148 149 ))
149 150 })
150 151 }
151 152
152 153 def clearambiguoustimes(
153 154 &self,
154 155 files: PyObject,
155 156 now: PyObject
156 157 ) -> PyResult<PyObject> {
157 158 let files: PyResult<Vec<HgPathBuf>> = files
158 159 .iter(py)?
159 160 .map(|filename| {
160 161 Ok(HgPathBuf::from_bytes(
161 162 filename?.extract::<PyBytes>(py)?.data(py),
162 163 ))
163 164 })
164 165 .collect();
165 166 self.inner_shared(py).borrow_mut()?
166 167 .clear_ambiguous_times(files?, now.extract(py)?);
167 168 Ok(py.None())
168 169 }
169 170
170 171 // TODO share the reference
171 172 def nonnormalentries(&self) -> PyResult<PyObject> {
172 173 let (non_normal, other_parent) =
173 174 self.inner_shared(py).borrow().non_normal_other_parent_entries();
174 175
175 176 let locals = PyDict::new(py);
176 177 locals.set_item(
177 178 py,
178 179 "non_normal",
179 180 non_normal
180 181 .iter()
181 182 .map(|v| PyBytes::new(py, v.as_ref()))
182 183 .collect::<Vec<PyBytes>>()
183 184 .to_py_object(py),
184 185 )?;
185 186 locals.set_item(
186 187 py,
187 188 "other_parent",
188 189 other_parent
189 190 .iter()
190 191 .map(|v| PyBytes::new(py, v.as_ref()))
191 192 .collect::<Vec<PyBytes>>()
192 193 .to_py_object(py),
193 194 )?;
194 195
195 196 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
196 197 }
197 198
198 199 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
199 200 let d = d.extract::<PyBytes>(py)?;
200 201 Ok(self.inner_shared(py).borrow_mut()?
201 202 .has_tracked_dir(HgPath::new(d.data(py)))
202 203 .to_py_object(py))
203 204 }
204 205
205 206 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
206 207 let d = d.extract::<PyBytes>(py)?;
207 208 Ok(self.inner_shared(py).borrow_mut()?
208 209 .has_dir(HgPath::new(d.data(py)))
209 210 .to_py_object(py))
210 211 }
211 212
212 213 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
213 214 self.inner_shared(py).borrow_mut()?
214 215 .parents(st.extract::<PyBytes>(py)?.data(py))
215 216 .and_then(|d| {
216 217 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
217 218 .to_py_object(py))
218 219 })
219 220 .or_else(|_| {
220 221 Err(PyErr::new::<exc::OSError, _>(
221 222 py,
222 223 "Dirstate error".to_string(),
223 224 ))
224 225 })
225 226 }
226 227
227 228 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
228 229 let p1 = extract_node_id(py, &p1)?;
229 230 let p2 = extract_node_id(py, &p2)?;
230 231
231 232 self.inner_shared(py).borrow_mut()?
232 233 .set_parents(&DirstateParents { p1, p2 });
233 234 Ok(py.None())
234 235 }
235 236
236 237 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
237 238 match self.inner_shared(py).borrow_mut()?
238 239 .read(st.extract::<PyBytes>(py)?.data(py))
239 240 {
240 241 Ok(Some(parents)) => Ok(Some(
241 242 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
242 243 .to_py_object(py)
243 244 .into_object(),
244 245 )),
245 246 Ok(None) => Ok(Some(py.None())),
246 247 Err(_) => Err(PyErr::new::<exc::OSError, _>(
247 248 py,
248 249 "Dirstate error".to_string(),
249 250 )),
250 251 }
251 252 }
252 253 def write(
253 254 &self,
254 255 p1: PyObject,
255 256 p2: PyObject,
256 257 now: PyObject
257 258 ) -> PyResult<PyBytes> {
258 259 let now = Duration::new(now.extract(py)?, 0);
259 260 let parents = DirstateParents {
260 261 p1: extract_node_id(py, &p1)?,
261 262 p2: extract_node_id(py, &p2)?,
262 263 };
263 264
264 265 match self.inner_shared(py).borrow_mut()?.pack(parents, now) {
265 266 Ok(packed) => Ok(PyBytes::new(py, &packed)),
266 267 Err(_) => Err(PyErr::new::<exc::OSError, _>(
267 268 py,
268 269 "Dirstate error".to_string(),
269 270 )),
270 271 }
271 272 }
272 273
273 274 def filefoldmapasdict(&self) -> PyResult<PyDict> {
274 275 let dict = PyDict::new(py);
275 276 for (key, value) in
276 277 self.inner_shared(py).borrow_mut()?.build_file_fold_map().iter()
277 278 {
278 279 dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?;
279 280 }
280 281 Ok(dict)
281 282 }
282 283
283 284 def __len__(&self) -> PyResult<usize> {
284 285 Ok(self.inner_shared(py).borrow().len())
285 286 }
286 287
287 288 def __contains__(&self, key: PyObject) -> PyResult<bool> {
288 289 let key = key.extract::<PyBytes>(py)?;
289 290 Ok(self.inner_shared(py).borrow().contains_key(HgPath::new(key.data(py))))
290 291 }
291 292
292 293 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
293 294 let key = key.extract::<PyBytes>(py)?;
294 295 let key = HgPath::new(key.data(py));
295 296 match self.inner_shared(py).borrow().get(key) {
296 297 Some(entry) => {
297 298 Ok(make_dirstate_tuple(py, entry)?)
298 299 },
299 300 None => Err(PyErr::new::<exc::KeyError, _>(
300 301 py,
301 302 String::from_utf8_lossy(key.as_bytes()),
302 303 )),
303 304 }
304 305 }
305 306
306 307 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
307 308 let leaked_ref = self.inner_shared(py).leak_immutable();
308 309 DirstateMapKeysIterator::from_inner(
309 310 py,
310 311 unsafe { leaked_ref.map(py, |o| o.iter()) },
311 312 )
312 313 }
313 314
314 315 def items(&self) -> PyResult<DirstateMapItemsIterator> {
315 316 let leaked_ref = self.inner_shared(py).leak_immutable();
316 317 DirstateMapItemsIterator::from_inner(
317 318 py,
318 319 unsafe { leaked_ref.map(py, |o| o.iter()) },
319 320 )
320 321 }
321 322
322 323 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
323 324 let leaked_ref = self.inner_shared(py).leak_immutable();
324 325 DirstateMapKeysIterator::from_inner(
325 326 py,
326 327 unsafe { leaked_ref.map(py, |o| o.iter()) },
327 328 )
328 329 }
329 330
330 331 def getdirs(&self) -> PyResult<Dirs> {
331 332 // TODO don't copy, share the reference
332 333 self.inner_shared(py).borrow_mut()?.set_dirs();
333 334 Dirs::from_inner(
334 335 py,
335 336 DirsMultiset::from_dirstate(
336 337 &self.inner_shared(py).borrow(),
337 338 Some(EntryState::Removed),
338 339 ),
339 340 )
340 341 }
341 342 def getalldirs(&self) -> PyResult<Dirs> {
342 343 // TODO don't copy, share the reference
343 344 self.inner_shared(py).borrow_mut()?.set_all_dirs();
344 345 Dirs::from_inner(
345 346 py,
346 347 DirsMultiset::from_dirstate(
347 348 &self.inner_shared(py).borrow(),
348 349 None,
349 350 ),
350 351 )
351 352 }
352 353
353 354 // TODO all copymap* methods, see docstring above
354 355 def copymapcopy(&self) -> PyResult<PyDict> {
355 356 let dict = PyDict::new(py);
356 357 for (key, value) in self.inner_shared(py).borrow().copy_map.iter() {
357 358 dict.set_item(
358 359 py,
359 360 PyBytes::new(py, key.as_ref()),
360 361 PyBytes::new(py, value.as_ref()),
361 362 )?;
362 363 }
363 364 Ok(dict)
364 365 }
365 366
366 367 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
367 368 let key = key.extract::<PyBytes>(py)?;
368 369 match self.inner_shared(py).borrow().copy_map.get(HgPath::new(key.data(py))) {
369 370 Some(copy) => Ok(PyBytes::new(py, copy.as_ref())),
370 371 None => Err(PyErr::new::<exc::KeyError, _>(
371 372 py,
372 373 String::from_utf8_lossy(key.data(py)),
373 374 )),
374 375 }
375 376 }
376 377 def copymap(&self) -> PyResult<CopyMap> {
377 378 CopyMap::from_inner(py, self.clone_ref(py))
378 379 }
379 380
380 381 def copymaplen(&self) -> PyResult<usize> {
381 382 Ok(self.inner_shared(py).borrow().copy_map.len())
382 383 }
383 384 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
384 385 let key = key.extract::<PyBytes>(py)?;
385 386 Ok(self
386 387 .inner_shared(py)
387 388 .borrow()
388 389 .copy_map
389 390 .contains_key(HgPath::new(key.data(py))))
390 391 }
391 392 def copymapget(
392 393 &self,
393 394 key: PyObject,
394 395 default: Option<PyObject>
395 396 ) -> PyResult<Option<PyObject>> {
396 397 let key = key.extract::<PyBytes>(py)?;
397 398 match self
398 399 .inner_shared(py)
399 400 .borrow()
400 401 .copy_map
401 402 .get(HgPath::new(key.data(py)))
402 403 {
403 404 Some(copy) => Ok(Some(
404 405 PyBytes::new(py, copy.as_ref()).into_object(),
405 406 )),
406 407 None => Ok(default),
407 408 }
408 409 }
409 410 def copymapsetitem(
410 411 &self,
411 412 key: PyObject,
412 413 value: PyObject
413 414 ) -> PyResult<PyObject> {
414 415 let key = key.extract::<PyBytes>(py)?;
415 416 let value = value.extract::<PyBytes>(py)?;
416 417 self.inner_shared(py).borrow_mut()?.copy_map.insert(
417 418 HgPathBuf::from_bytes(key.data(py)),
418 419 HgPathBuf::from_bytes(value.data(py)),
419 420 );
420 421 Ok(py.None())
421 422 }
422 423 def copymappop(
423 424 &self,
424 425 key: PyObject,
425 426 default: Option<PyObject>
426 427 ) -> PyResult<Option<PyObject>> {
427 428 let key = key.extract::<PyBytes>(py)?;
428 429 match self
429 430 .inner_shared(py)
430 431 .borrow_mut()?
431 432 .copy_map
432 433 .remove(HgPath::new(key.data(py)))
433 434 {
434 435 Some(_) => Ok(None),
435 436 None => Ok(default),
436 437 }
437 438 }
438 439
439 440 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
440 441 let leaked_ref = self.inner_shared(py).leak_immutable();
441 442 CopyMapKeysIterator::from_inner(
442 443 py,
443 444 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
444 445 )
445 446 }
446 447
447 448 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
448 449 let leaked_ref = self.inner_shared(py).leak_immutable();
449 450 CopyMapItemsIterator::from_inner(
450 451 py,
451 452 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
452 453 )
453 454 }
454 455
455 456 });
456 457
457 458 impl DirstateMap {
458 459 pub fn get_inner<'a>(
459 460 &'a self,
460 461 py: Python<'a>,
461 462 ) -> Ref<'a, RustDirstateMap> {
462 463 self.inner_shared(py).borrow()
463 464 }
464 465 fn translate_key(
465 466 py: Python,
466 467 res: (&HgPathBuf, &DirstateEntry),
467 468 ) -> PyResult<Option<PyBytes>> {
468 469 Ok(Some(PyBytes::new(py, res.0.as_ref())))
469 470 }
470 471 fn translate_key_value(
471 472 py: Python,
472 473 res: (&HgPathBuf, &DirstateEntry),
473 474 ) -> PyResult<Option<(PyBytes, PyObject)>> {
474 475 let (f, entry) = res;
475 476 Ok(Some((
476 477 PyBytes::new(py, f.as_ref()),
477 478 make_dirstate_tuple(py, entry)?,
478 479 )))
479 480 }
480 481 }
481 482
482 483 py_shared_ref!(DirstateMap, RustDirstateMap, inner, inner_shared);
483 484
484 485 py_shared_iterator!(
485 486 DirstateMapKeysIterator,
486 487 PyLeaked<StateMapIter<'static>>,
487 488 DirstateMap::translate_key,
488 489 Option<PyBytes>
489 490 );
490 491
491 492 py_shared_iterator!(
492 493 DirstateMapItemsIterator,
493 494 PyLeaked<StateMapIter<'static>>,
494 495 DirstateMap::translate_key_value,
495 496 Option<(PyBytes, PyObject)>
496 497 );
497 498
498 499 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
499 500 let bytes = obj.extract::<PyBytes>(py)?;
500 501 match bytes.data(py).try_into() {
501 502 Ok(s) => Ok(s),
502 503 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
503 504 }
504 505 }
General Comments 0
You need to be logged in to leave comments. Login now