comparison rust/hg-core/src/revlog/node.rs @ 46428:5893706af3de

rust: Simplify error type for reading hex node IDs If a string is not valid hexadecimal it’s not that useful to track the precise reason. Differential Revision: https://phab.mercurial-scm.org/D9861
author Simon Sapin <simon.sapin@octobus.net>
date Mon, 25 Jan 2021 12:28:39 +0100
parents 6380efb82191
children e61c2dc6e1c2
comparison
equal deleted inserted replaced
46427:6380efb82191 46428:5893706af3de
7 //! 7 //!
8 //! In Mercurial code base, it is customary to call "a node" the binary SHA 8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
9 //! of a revision. 9 //! of a revision.
10 10
11 use bytes_cast::BytesCast; 11 use bytes_cast::BytesCast;
12 use hex::{self, FromHex, FromHexError}; 12 use hex::{self, FromHex};
13 use std::convert::TryFrom; 13 use std::convert::TryFrom;
14 use std::fmt; 14 use std::fmt;
15 15
16 /// The length in bytes of a `Node` 16 /// The length in bytes of a `Node`
17 /// 17 ///
45 /// All other callers outside of unit tests should just handle `Node` values 45 /// All other callers outside of unit tests should just handle `Node` values
46 /// and never make any assumption on the actual length, using [`nybbles_len`] 46 /// and never make any assumption on the actual length, using [`nybbles_len`]
47 /// if they need a loop boundary. 47 /// if they need a loop boundary.
48 /// 48 ///
49 /// All methods that create a `Node` either take a type that enforces 49 /// All methods that create a `Node` either take a type that enforces
50 /// the size or fail immediately at runtime with [`ExactLengthRequired`]. 50 /// the size or return an error at runtime.
51 /// 51 ///
52 /// [`nybbles_len`]: #method.nybbles_len 52 /// [`nybbles_len`]: #method.nybbles_len
53 /// [`ExactLengthRequired`]: struct.NodeError#variant.ExactLengthRequired
54 #[derive(Clone, Debug, PartialEq, BytesCast)] 53 #[derive(Clone, Debug, PartialEq, BytesCast)]
55 #[repr(transparent)] 54 #[repr(transparent)]
56 pub struct Node { 55 pub struct Node {
57 data: NodeData, 56 data: NodeData,
58 } 57 }
88 } 87 }
89 Ok(()) 88 Ok(())
90 } 89 }
91 } 90 }
92 91
93 #[derive(Debug, PartialEq)] 92 #[derive(Debug)]
94 pub enum NodeError { 93 pub struct FromHexError;
95 ExactLengthRequired(usize, String),
96 PrefixTooLong(String),
97 HexError(FromHexError, String),
98 }
99 94
100 /// Low level utility function, also for prefixes 95 /// Low level utility function, also for prefixes
101 fn get_nybble(s: &[u8], i: usize) -> u8 { 96 fn get_nybble(s: &[u8], i: usize) -> u8 {
102 if i % 2 == 0 { 97 if i % 2 == 0 {
103 s[i / 2] >> 4 98 s[i / 2] >> 4
126 /// 121 ///
127 /// Exact length is required. 122 /// Exact length is required.
128 /// 123 ///
129 /// To be used in FFI and I/O only, in order to facilitate future 124 /// To be used in FFI and I/O only, in order to facilitate future
130 /// changes of hash format. 125 /// changes of hash format.
131 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, NodeError> { 126 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, FromHexError> {
132 Ok(NodeData::from_hex(hex.as_ref()) 127 Ok(NodeData::from_hex(hex.as_ref())
133 .map_err(|e| NodeError::from((e, hex)))? 128 .map_err(|_| FromHexError)?
134 .into()) 129 .into())
135 } 130 }
136 131
137 /// Provide access to binary data 132 /// Provide access to binary data
138 /// 133 ///
139 /// This is needed by FFI layers, for instance to return expected 134 /// This is needed by FFI layers, for instance to return expected
140 /// binary values to Python. 135 /// binary values to Python.
141 pub fn as_bytes(&self) -> &[u8] { 136 pub fn as_bytes(&self) -> &[u8] {
142 &self.data 137 &self.data
143 }
144 }
145
146 impl<T: AsRef<[u8]>> From<(FromHexError, T)> for NodeError {
147 fn from(err_offender: (FromHexError, T)) -> Self {
148 let (err, offender) = err_offender;
149 let offender = String::from_utf8_lossy(offender.as_ref()).into_owned();
150 match err {
151 FromHexError::InvalidStringLength => {
152 NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, offender)
153 }
154 _ => NodeError::HexError(err, offender),
155 }
156 } 138 }
157 } 139 }
158 140
159 /// The beginning of a binary revision SHA. 141 /// The beginning of a binary revision SHA.
160 /// 142 ///
173 /// Similarly to `hex::decode`, can be used with Unicode string types 155 /// Similarly to `hex::decode`, can be used with Unicode string types
174 /// (`String`, `&str`) as well as bytes. 156 /// (`String`, `&str`) as well as bytes.
175 /// 157 ///
176 /// To be used in FFI and I/O only, in order to facilitate future 158 /// To be used in FFI and I/O only, in order to facilitate future
177 /// changes of hash format. 159 /// changes of hash format.
178 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, NodeError> { 160 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, FromHexError> {
179 let hex = hex.as_ref(); 161 let hex = hex.as_ref();
180 let len = hex.len(); 162 let len = hex.len();
181 if len > NODE_NYBBLES_LENGTH { 163 if len > NODE_NYBBLES_LENGTH {
182 return Err(NodeError::PrefixTooLong( 164 return Err(FromHexError);
183 String::from_utf8_lossy(hex).to_owned().to_string(),
184 ));
185 } 165 }
186 166
187 let is_odd = len % 2 == 1; 167 let is_odd = len % 2 == 1;
188 let even_part = if is_odd { &hex[..len - 1] } else { hex }; 168 let even_part = if is_odd { &hex[..len - 1] } else { hex };
189 let mut buf: Vec<u8> = 169 let mut buf: Vec<u8> =
190 Vec::from_hex(&even_part).map_err(|e| (e, hex))?; 170 Vec::from_hex(&even_part).map_err(|_| FromHexError)?;
191 171
192 if is_odd { 172 if is_odd {
193 let latest_char = char::from(hex[len - 1]); 173 let latest_char = char::from(hex[len - 1]);
194 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| { 174 let latest_nybble =
195 ( 175 latest_char.to_digit(16).ok_or_else(|| FromHexError)? as u8;
196 FromHexError::InvalidHexCharacter {
197 c: latest_char,
198 index: len - 1,
199 },
200 hex,
201 )
202 })? as u8;
203 buf.push(latest_nybble << 4); 176 buf.push(latest_nybble << 4);
204 } 177 }
205 Ok(NodePrefix { buf, is_odd }) 178 Ok(NodePrefix { buf, is_odd })
206 } 179 }
207 180
327 hex_pad_right("0123456789abcdeffedcba9876543210deadbeef") 300 hex_pad_right("0123456789abcdeffedcba9876543210deadbeef")
328 } 301 }
329 302
330 #[test] 303 #[test]
331 fn test_node_from_hex() { 304 fn test_node_from_hex() {
332 assert_eq!(Node::from_hex(&sample_node_hex()), Ok(sample_node())); 305 assert_eq!(Node::from_hex(&sample_node_hex()).unwrap(), sample_node());
333 306
334 let mut short = hex_pad_right("0123"); 307 let mut short = hex_pad_right("0123");
335 short.pop(); 308 short.pop();
336 short.pop(); 309 short.pop();
337 assert_eq!( 310 assert!(Node::from_hex(&short).is_err());
338 Node::from_hex(&short),
339 Err(NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, short)),
340 );
341 311
342 let not_hex = hex_pad_right("012... oops"); 312 let not_hex = hex_pad_right("012... oops");
343 assert_eq!( 313 assert!(Node::from_hex(&not_hex).is_err(),);
344 Node::from_hex(&not_hex),
345 Err(NodeError::HexError(
346 FromHexError::InvalidHexCharacter { c: '.', index: 3 },
347 not_hex,
348 )),
349 );
350 } 314 }
351 315
352 #[test] 316 #[test]
353 fn test_node_encode_hex() { 317 fn test_node_encode_hex() {
354 assert_eq!(format!("{:x}", sample_node()), sample_node_hex()); 318 assert_eq!(format!("{:x}", sample_node()), sample_node_hex());
355 } 319 }
356 320
357 #[test] 321 #[test]
358 fn test_prefix_from_hex() -> Result<(), NodeError> { 322 fn test_prefix_from_hex() -> Result<(), FromHexError> {
359 assert_eq!( 323 assert_eq!(
360 NodePrefix::from_hex("0e1")?, 324 NodePrefix::from_hex("0e1")?,
361 NodePrefix { 325 NodePrefix {
362 buf: vec![14, 16], 326 buf: vec![14, 16],
363 is_odd: true 327 is_odd: true
384 Ok(()) 348 Ok(())
385 } 349 }
386 350
387 #[test] 351 #[test]
388 fn test_prefix_from_hex_errors() { 352 fn test_prefix_from_hex_errors() {
389 assert_eq!( 353 assert!(NodePrefix::from_hex("testgr").is_err());
390 NodePrefix::from_hex("testgr"),
391 Err(NodeError::HexError(
392 FromHexError::InvalidHexCharacter { c: 't', index: 0 },
393 "testgr".to_string()
394 ))
395 );
396 let mut long = format!("{:x}", NULL_NODE); 354 let mut long = format!("{:x}", NULL_NODE);
397 long.push('c'); 355 long.push('c');
398 match NodePrefix::from_hex(&long) 356 assert!(NodePrefix::from_hex(&long).is_err())
399 .expect_err("should be refused as too long") 357 }
400 { 358
401 NodeError::PrefixTooLong(s) => assert_eq!(s, long), 359 #[test]
402 err => panic!(format!("Should have been TooLong, got {:?}", err)), 360 fn test_is_prefix_of() -> Result<(), FromHexError> {
403 }
404 }
405
406 #[test]
407 fn test_is_prefix_of() -> Result<(), NodeError> {
408 let mut node_data = [0; NODE_BYTES_LENGTH]; 361 let mut node_data = [0; NODE_BYTES_LENGTH];
409 node_data[0] = 0x12; 362 node_data[0] = 0x12;
410 node_data[1] = 0xca; 363 node_data[1] = 0xca;
411 let node = Node::from(node_data); 364 let node = Node::from(node_data);
412 assert!(NodePrefix::from_hex("12")?.borrow().is_prefix_of(&node)); 365 assert!(NodePrefix::from_hex("12")?.borrow().is_prefix_of(&node));
415 assert!(!NodePrefix::from_hex("12d")?.borrow().is_prefix_of(&node)); 368 assert!(!NodePrefix::from_hex("12d")?.borrow().is_prefix_of(&node));
416 Ok(()) 369 Ok(())
417 } 370 }
418 371
419 #[test] 372 #[test]
420 fn test_get_nybble() -> Result<(), NodeError> { 373 fn test_get_nybble() -> Result<(), FromHexError> {
421 let prefix = NodePrefix::from_hex("dead6789cafe")?; 374 let prefix = NodePrefix::from_hex("dead6789cafe")?;
422 assert_eq!(prefix.borrow().get_nybble(0), 13); 375 assert_eq!(prefix.borrow().get_nybble(0), 13);
423 assert_eq!(prefix.borrow().get_nybble(7), 9); 376 assert_eq!(prefix.borrow().get_nybble(7), 9);
424 Ok(()) 377 Ok(())
425 } 378 }