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