##// END OF EJS Templates
rust-dirstate: use EntryState enum instead of literals...
Raphaël Gomès -
r42976:7102ef7d default draft
parent child Browse files
Show More
@@ -5,7 +5,9 b''
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::DirstateParseError;
8 use std::collections::HashMap;
9 use std::collections::HashMap;
10 use std::convert::TryFrom;
9
11
10 pub mod dirs_multiset;
12 pub mod dirs_multiset;
11 pub mod parsers;
13 pub mod parsers;
@@ -21,7 +23,7 b' pub struct DirstateParents {'
21 /// comes first.
23 /// comes first.
22 #[derive(Debug, PartialEq, Copy, Clone)]
24 #[derive(Debug, PartialEq, Copy, Clone)]
23 pub struct DirstateEntry {
25 pub struct DirstateEntry {
24 pub state: i8,
26 pub state: EntryState,
25 pub mode: i32,
27 pub mode: i32,
26 pub mtime: i32,
28 pub mtime: i32,
27 pub size: i32,
29 pub size: i32,
@@ -36,3 +38,42 b" pub enum DirsIterable<'a> {"
36 Dirstate(&'a HashMap<Vec<u8>, DirstateEntry>),
38 Dirstate(&'a HashMap<Vec<u8>, DirstateEntry>),
37 Manifest(&'a Vec<Vec<u8>>),
39 Manifest(&'a Vec<Vec<u8>>),
38 }
40 }
41
42 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
43 pub enum EntryState {
44 Normal,
45 Added,
46 Removed,
47 Merged,
48 Unknown,
49 }
50
51 impl TryFrom<u8> for EntryState {
52 type Error = DirstateParseError;
53
54 fn try_from(value: u8) -> Result<Self, Self::Error> {
55 match value {
56 b'n' => Ok(EntryState::Normal),
57 b'a' => Ok(EntryState::Added),
58 b'r' => Ok(EntryState::Removed),
59 b'm' => Ok(EntryState::Merged),
60 b'?' => Ok(EntryState::Unknown),
61 _ => Err(DirstateParseError::CorruptedEntry(format!(
62 "Incorrect entry state {}",
63 value
64 ))),
65 }
66 }
67 }
68
69 impl Into<u8> for EntryState {
70 fn into(self) -> u8 {
71 match self {
72 EntryState::Normal => b'n',
73 EntryState::Added => b'a',
74 EntryState::Removed => b'r',
75 EntryState::Merged => b'm',
76 EntryState::Unknown => b'?',
77 }
78 }
79 }
@@ -8,7 +8,10 b''
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::files, DirsIterable, DirstateEntry, DirstateMapError};
11 use crate::{
12 dirstate::EntryState, utils::files, DirsIterable, DirstateEntry,
13 DirstateMapError,
14 };
12 use std::collections::hash_map::{Entry, Iter};
15 use std::collections::hash_map::{Entry, Iter};
13 use std::collections::HashMap;
16 use std::collections::HashMap;
14
17
@@ -21,7 +24,10 b' impl DirsMultiset {'
21 /// Initializes the multiset from a dirstate or a manifest.
24 /// Initializes the multiset from a dirstate or a manifest.
22 ///
25 ///
23 /// If `skip_state` is provided, skips dirstate entries with equal state.
26 /// If `skip_state` is provided, skips dirstate entries with equal state.
24 pub fn new(iterable: DirsIterable, skip_state: Option<i8>) -> Self {
27 pub fn new(
28 iterable: DirsIterable,
29 skip_state: Option<EntryState>,
30 ) -> Self {
25 let mut multiset = DirsMultiset {
31 let mut multiset = DirsMultiset {
26 inner: HashMap::new(),
32 inner: HashMap::new(),
27 };
33 };
@@ -257,7 +263,7 b' mod tests {'
257 (
263 (
258 f.as_bytes().to_vec(),
264 f.as_bytes().to_vec(),
259 DirstateEntry {
265 DirstateEntry {
260 state: 0,
266 state: EntryState::Normal,
261 mode: 0,
267 mode: 0,
262 mtime: 0,
268 mtime: 0,
263 size: 0,
269 size: 0,
@@ -290,28 +296,33 b' mod tests {'
290 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
296 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
291 .collect();
297 .collect();
292
298
293 let new = DirsMultiset::new(Manifest(&input_vec), Some('n' as i8));
299 let new =
300 DirsMultiset::new(Manifest(&input_vec), Some(EntryState::Normal));
294 let expected = DirsMultiset {
301 let expected = DirsMultiset {
295 inner: expected_inner,
302 inner: expected_inner,
296 };
303 };
297 // Skip does not affect a manifest
304 // Skip does not affect a manifest
298 assert_eq!(expected, new);
305 assert_eq!(expected, new);
299
306
300 let input_map =
307 let input_map = [
301 [("a/", 'n'), ("a/b/", 'n'), ("a/c", 'r'), ("a/d/", 'm')]
308 ("a/", EntryState::Normal),
302 .iter()
309 ("a/b/", EntryState::Normal),
303 .map(|(f, state)| {
310 ("a/c", EntryState::Removed),
304 (
311 ("a/d/", EntryState::Merged),
305 f.as_bytes().to_vec(),
312 ]
306 DirstateEntry {
313 .iter()
307 state: *state as i8,
314 .map(|(f, state)| {
308 mode: 0,
315 (
309 mtime: 0,
316 f.as_bytes().to_vec(),
310 size: 0,
317 DirstateEntry {
311 },
318 state: *state,
312 )
319 mode: 0,
313 })
320 mtime: 0,
314 .collect();
321 size: 0,
322 },
323 )
324 })
325 .collect();
315
326
316 // "a" incremented with "a/c" and "a/d/"
327 // "a" incremented with "a/c" and "a/d/"
317 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
328 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
@@ -319,10 +330,12 b' mod tests {'
319 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
330 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
320 .collect();
331 .collect();
321
332
322 let new = DirsMultiset::new(Dirstate(&input_map), Some('n' as i8));
333 let new =
334 DirsMultiset::new(Dirstate(&input_map), Some(EntryState::Normal));
323 let expected = DirsMultiset {
335 let expected = DirsMultiset {
324 inner: expected_inner,
336 inner: expected_inner,
325 };
337 };
326 assert_eq!(expected, new);
338 assert_eq!(expected, new);
327 }
339 }
340
328 }
341 }
@@ -4,12 +4,12 b''
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 use crate::{
6 use crate::{
7 dirstate::{CopyMap, StateMap},
7 dirstate::{CopyMap, EntryState, StateMap},
8 utils::copy_into_array,
8 utils::copy_into_array,
9 DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError,
9 DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError,
10 };
10 };
11 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
11 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
12 use std::convert::TryInto;
12 use std::convert::{TryFrom, TryInto};
13 use std::io::Cursor;
13 use std::io::Cursor;
14 use std::time::Duration;
14 use std::time::Duration;
15
15
@@ -42,7 +42,7 b' pub fn parse_dirstate('
42 let entry_bytes = &contents[curr_pos..];
42 let entry_bytes = &contents[curr_pos..];
43
43
44 let mut cursor = Cursor::new(entry_bytes);
44 let mut cursor = Cursor::new(entry_bytes);
45 let state = cursor.read_i8()?;
45 let state = EntryState::try_from(cursor.read_u8()?)?;
46 let mode = cursor.read_i32::<BigEndian>()?;
46 let mode = cursor.read_i32::<BigEndian>()?;
47 let size = cursor.read_i32::<BigEndian>()?;
47 let size = cursor.read_i32::<BigEndian>()?;
48 let mtime = cursor.read_i32::<BigEndian>()?;
48 let mtime = cursor.read_i32::<BigEndian>()?;
@@ -109,7 +109,7 b' pub fn pack_dirstate('
109 for (ref filename, entry) in state_map.iter() {
109 for (ref filename, entry) in state_map.iter() {
110 let mut new_filename: Vec<u8> = filename.to_vec();
110 let mut new_filename: Vec<u8> = filename.to_vec();
111 let mut new_mtime: i32 = entry.mtime;
111 let mut new_mtime: i32 = entry.mtime;
112 if entry.state == 'n' as i8 && entry.mtime == now.into() {
112 if entry.state == EntryState::Normal && entry.mtime == now {
113 // The file was last modified "simultaneously" with the current
113 // The file was last modified "simultaneously" with the current
114 // write to dirstate (i.e. within the same second for file-
114 // write to dirstate (i.e. within the same second for file-
115 // systems with a granularity of 1 sec). This commonly happens
115 // systems with a granularity of 1 sec). This commonly happens
@@ -134,7 +134,7 b' pub fn pack_dirstate('
134 new_filename.extend(copy);
134 new_filename.extend(copy);
135 }
135 }
136
136
137 packed.write_i8(entry.state)?;
137 packed.write_u8(entry.state.into())?;
138 packed.write_i32::<BigEndian>(entry.mode)?;
138 packed.write_i32::<BigEndian>(entry.mode)?;
139 packed.write_i32::<BigEndian>(entry.size)?;
139 packed.write_i32::<BigEndian>(entry.size)?;
140 packed.write_i32::<BigEndian>(new_mtime)?;
140 packed.write_i32::<BigEndian>(new_mtime)?;
@@ -150,6 +150,7 b' pub fn pack_dirstate('
150
150
151 Ok(packed)
151 Ok(packed)
152 }
152 }
153
153 #[cfg(test)]
154 #[cfg(test)]
154 mod tests {
155 mod tests {
155 use super::*;
156 use super::*;
@@ -178,7 +179,7 b' mod tests {'
178 let expected_state_map: StateMap = [(
179 let expected_state_map: StateMap = [(
179 b"f1".to_vec(),
180 b"f1".to_vec(),
180 DirstateEntry {
181 DirstateEntry {
181 state: 'n' as i8,
182 state: EntryState::Normal,
182 mode: 0o644,
183 mode: 0o644,
183 size: 0,
184 size: 0,
184 mtime: 791231220,
185 mtime: 791231220,
@@ -215,7 +216,7 b' mod tests {'
215 let expected_state_map: StateMap = [(
216 let expected_state_map: StateMap = [(
216 b"f1".to_vec(),
217 b"f1".to_vec(),
217 DirstateEntry {
218 DirstateEntry {
218 state: 'n' as i8,
219 state: EntryState::Normal,
219 mode: 0o644,
220 mode: 0o644,
220 size: 0,
221 size: 0,
221 mtime: 791231220,
222 mtime: 791231220,
@@ -253,7 +254,7 b' mod tests {'
253 let mut state_map: StateMap = [(
254 let mut state_map: StateMap = [(
254 b"f1".to_vec(),
255 b"f1".to_vec(),
255 DirstateEntry {
256 DirstateEntry {
256 state: 'n' as i8,
257 state: EntryState::Normal,
257 mode: 0o644,
258 mode: 0o644,
258 size: 0,
259 size: 0,
259 mtime: 791231220,
260 mtime: 791231220,
@@ -293,7 +294,7 b' mod tests {'
293 (
294 (
294 b"f1".to_vec(),
295 b"f1".to_vec(),
295 DirstateEntry {
296 DirstateEntry {
296 state: 'n' as i8,
297 state: EntryState::Normal,
297 mode: 0o644,
298 mode: 0o644,
298 size: 0,
299 size: 0,
299 mtime: 791231220,
300 mtime: 791231220,
@@ -302,7 +303,7 b' mod tests {'
302 (
303 (
303 b"f2".to_vec(),
304 b"f2".to_vec(),
304 DirstateEntry {
305 DirstateEntry {
305 state: 'm' as i8,
306 state: EntryState::Merged,
306 mode: 0o777,
307 mode: 0o777,
307 size: 1000,
308 size: 1000,
308 mtime: 791231220,
309 mtime: 791231220,
@@ -311,7 +312,7 b' mod tests {'
311 (
312 (
312 b"f3".to_vec(),
313 b"f3".to_vec(),
313 DirstateEntry {
314 DirstateEntry {
314 state: 'r' as i8,
315 state: EntryState::Removed,
315 mode: 0o644,
316 mode: 0o644,
316 size: 234553,
317 size: 234553,
317 mtime: 791231220,
318 mtime: 791231220,
@@ -320,7 +321,7 b' mod tests {'
320 (
321 (
321 b"f4\xF6".to_vec(),
322 b"f4\xF6".to_vec(),
322 DirstateEntry {
323 DirstateEntry {
323 state: 'a' as i8,
324 state: EntryState::Added,
324 mode: 0o644,
325 mode: 0o644,
325 size: -1,
326 size: -1,
326 mtime: -1,
327 mtime: -1,
@@ -362,7 +363,7 b' mod tests {'
362 let mut state_map: StateMap = [(
363 let mut state_map: StateMap = [(
363 b"f1".to_vec(),
364 b"f1".to_vec(),
364 DirstateEntry {
365 DirstateEntry {
365 state: 'n' as i8,
366 state: EntryState::Normal,
366 mode: 0o644,
367 mode: 0o644,
367 size: 0,
368 size: 0,
368 mtime: 15000000,
369 mtime: 15000000,
@@ -397,7 +398,7 b' mod tests {'
397 [(
398 [(
398 b"f1".to_vec(),
399 b"f1".to_vec(),
399 DirstateEntry {
400 DirstateEntry {
400 state: 'n' as i8,
401 state: EntryState::Normal,
401 mode: 0o644,
402 mode: 0o644,
402 size: 0,
403 size: 0,
403 mtime: -1
404 mtime: -1
@@ -11,7 +11,8 b' pub mod testing; // unconditionally buil'
11 pub use dirstate::{
11 pub use dirstate::{
12 dirs_multiset::DirsMultiset,
12 dirs_multiset::DirsMultiset,
13 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
13 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
14 CopyMap, DirsIterable, DirstateEntry, DirstateParents, StateMap,
14 CopyMap, DirsIterable, DirstateEntry, DirstateParents, EntryState,
15 StateMap,
15 };
16 };
16 mod filepatterns;
17 mod filepatterns;
17 pub mod utils;
18 pub mod utils;
@@ -62,6 +63,24 b' pub enum DirstateParseError {'
62 Damaged,
63 Damaged,
63 }
64 }
64
65
66 impl From<std::io::Error> for DirstateParseError {
67 fn from(e: std::io::Error) -> Self {
68 DirstateParseError::CorruptedEntry(e.to_string())
69 }
70 }
71
72 impl ToString for DirstateParseError {
73 fn to_string(&self) -> String {
74 use crate::DirstateParseError::*;
75 match self {
76 TooLittleData => "Too little data for dirstate.".to_string(),
77 Overflow => "Overflow in dirstate.".to_string(),
78 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
79 Damaged => "Dirstate appears to be damaged.".to_string(),
80 }
81 }
82 }
83
65 #[derive(Debug, PartialEq)]
84 #[derive(Debug, PartialEq)]
66 pub enum DirstatePackError {
85 pub enum DirstatePackError {
67 CorruptedEntry(String),
86 CorruptedEntry(String),
@@ -69,21 +88,33 b' pub enum DirstatePackError {'
69 BadSize(usize, usize),
88 BadSize(usize, usize),
70 }
89 }
71
90
91 impl From<std::io::Error> for DirstatePackError {
92 fn from(e: std::io::Error) -> Self {
93 DirstatePackError::CorruptedEntry(e.to_string())
94 }
95 }
72 #[derive(Debug, PartialEq)]
96 #[derive(Debug, PartialEq)]
73 pub enum DirstateMapError {
97 pub enum DirstateMapError {
74 PathNotFound(Vec<u8>),
98 PathNotFound(Vec<u8>),
75 EmptyPath,
99 EmptyPath,
76 }
100 }
77
101
78 impl From<std::io::Error> for DirstatePackError {
102 pub enum DirstateError {
79 fn from(e: std::io::Error) -> Self {
103 Parse(DirstateParseError),
80 DirstatePackError::CorruptedEntry(e.to_string())
104 Pack(DirstatePackError),
105 Map(DirstateMapError),
106 IO(std::io::Error),
107 }
108
109 impl From<DirstateParseError> for DirstateError {
110 fn from(e: DirstateParseError) -> Self {
111 DirstateError::Parse(e)
81 }
112 }
82 }
113 }
83
114
84 impl From<std::io::Error> for DirstateParseError {
115 impl From<DirstatePackError> for DirstateError {
85 fn from(e: std::io::Error) -> Self {
116 fn from(e: DirstatePackError) -> Self {
86 DirstateParseError::CorruptedEntry(e.to_string())
117 DirstateError::Pack(e)
87 }
118 }
88 }
119 }
89
120
@@ -103,3 +134,15 b' impl From<std::io::Error> for PatternFil'
103 PatternFileError::IO(e)
134 PatternFileError::IO(e)
104 }
135 }
105 }
136 }
137
138 impl From<DirstateMapError> for DirstateError {
139 fn from(e: DirstateMapError) -> Self {
140 DirstateError::Map(e)
141 }
142 }
143
144 impl From<std::io::Error> for DirstateError {
145 fn from(e: std::io::Error) -> Self {
146 DirstateError::IO(e)
147 }
148 }
@@ -12,14 +12,16 b''
12 mod dirs_multiset;
12 mod dirs_multiset;
13 use crate::dirstate::dirs_multiset::Dirs;
13 use crate::dirstate::dirs_multiset::Dirs;
14 use cpython::{
14 use cpython::{
15 PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence, Python,
15 exc, PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence,
16 Python,
16 };
17 };
17 use hg::{DirstateEntry, StateMap};
18 use hg::{DirstateEntry, DirstateParseError, EntryState, StateMap};
18 use libc::{c_char, c_int};
19 use libc::{c_char, c_int};
19 #[cfg(feature = "python27")]
20 #[cfg(feature = "python27")]
20 use python27_sys::PyCapsule_Import;
21 use python27_sys::PyCapsule_Import;
21 #[cfg(feature = "python3")]
22 #[cfg(feature = "python3")]
22 use python3_sys::PyCapsule_Import;
23 use python3_sys::PyCapsule_Import;
24 use std::convert::TryFrom;
23 use std::ffi::CStr;
25 use std::ffi::CStr;
24 use std::mem::transmute;
26 use std::mem::transmute;
25
27
@@ -60,7 +62,11 b' pub fn extract_dirstate(py: Python, dmap'
60 .map(|(filename, stats)| {
62 .map(|(filename, stats)| {
61 let stats = stats.extract::<PySequence>(py)?;
63 let stats = stats.extract::<PySequence>(py)?;
62 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
64 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
63 let state = state.data(py)[0] as i8;
65 let state = EntryState::try_from(state.data(py)[0]).map_err(
66 |e: DirstateParseError| {
67 PyErr::new::<exc::ValueError, _>(py, e.to_string())
68 },
69 )?;
64 let mode = stats.get_item(py, 1)?.extract(py)?;
70 let mode = stats.get_item(py, 1)?.extract(py)?;
65 let size = stats.get_item(py, 2)?.extract(py)?;
71 let size = stats.get_item(py, 2)?.extract(py)?;
66 let mtime = stats.get_item(py, 3)?.extract(py)?;
72 let mtime = stats.get_item(py, 3)?.extract(py)?;
@@ -16,7 +16,11 b' use cpython::{'
16 };
16 };
17
17
18 use crate::dirstate::extract_dirstate;
18 use crate::dirstate::extract_dirstate;
19 use hg::{DirsIterable, DirsMultiset, DirstateMapError};
19 use hg::{
20 DirsIterable, DirsMultiset, DirstateMapError, DirstateParseError,
21 EntryState,
22 };
23 use std::convert::TryInto;
20
24
21 py_class!(pub class Dirs |py| {
25 py_class!(pub class Dirs |py| {
22 data dirs_map: RefCell<DirsMultiset>;
26 data dirs_map: RefCell<DirsMultiset>;
@@ -28,9 +32,15 b' py_class!(pub class Dirs |py| {'
28 map: PyObject,
32 map: PyObject,
29 skip: Option<PyObject> = None
33 skip: Option<PyObject> = None
30 ) -> PyResult<Self> {
34 ) -> PyResult<Self> {
31 let mut skip_state: Option<i8> = None;
35 let mut skip_state: Option<EntryState> = None;
32 if let Some(skip) = skip {
36 if let Some(skip) = skip {
33 skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8);
37 skip_state = Some(
38 skip.extract::<PyBytes>(py)?.data(py)[0]
39 .try_into()
40 .map_err(|e: DirstateParseError| {
41 PyErr::new::<exc::ValueError, _>(py, e.to_string())
42 })?,
43 );
34 }
44 }
35 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
45 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
36 let dirstate = extract_dirstate(py, &map)?;
46 let dirstate = extract_dirstate(py, &map)?;
@@ -37,11 +37,17 b' fn parse_dirstate_wrapper('
37 match parse_dirstate(&mut dirstate_map, &mut copies, st.data(py)) {
37 match parse_dirstate(&mut dirstate_map, &mut copies, st.data(py)) {
38 Ok(parents) => {
38 Ok(parents) => {
39 for (filename, entry) in dirstate_map {
39 for (filename, entry) in dirstate_map {
40 // Explicitly go through u8 first, then cast to
41 // platform-specific `c_char` because Into<u8> has a specific
42 // implementation while `as c_char` would just do a naive enum
43 // cast.
44 let state: u8 = entry.state.into();
45
40 dmap.set_item(
46 dmap.set_item(
41 py,
47 py,
42 PyBytes::new(py, &filename),
48 PyBytes::new(py, &filename),
43 decapsule_make_dirstate_tuple(py)?(
49 decapsule_make_dirstate_tuple(py)?(
44 entry.state as c_char,
50 state as c_char,
45 entry.mode,
51 entry.mode,
46 entry.size,
52 entry.size,
47 entry.mtime,
53 entry.mtime,
@@ -130,6 +136,11 b' fn pack_dirstate_wrapper('
130 },
136 },
131 ) in dirstate_map
137 ) in dirstate_map
132 {
138 {
139 // Explicitly go through u8 first, then cast to
140 // platform-specific `c_char` because Into<u8> has a specific
141 // implementation while `as c_char` would just do a naive enum
142 // cast.
143 let state: u8 = state.into();
133 dmap.set_item(
144 dmap.set_item(
134 py,
145 py,
135 PyBytes::new(py, &filename[..]),
146 PyBytes::new(py, &filename[..]),
General Comments 0
You need to be logged in to leave comments. Login now