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