rust: Simplify error type for reading hex node IDs
authorSimon Sapin <simon.sapin@octobus.net>
Mon, 25 Jan 2021 12:28:39 +0100
changeset 46428 5893706af3de
parent 46427 6380efb82191
child 46429 e61c2dc6e1c2
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
rust/hg-core/src/revlog.rs
rust/hg-core/src/revlog/node.rs
rust/hg-core/src/revlog/nodemap.rs
rust/hg-cpython/src/revlog.rs
--- a/rust/hg-core/src/revlog.rs	Mon Jan 25 12:00:23 2021 +0100
+++ b/rust/hg-core/src/revlog.rs	Mon Jan 25 12:28:39 2021 +0100
@@ -9,7 +9,7 @@
 pub mod nodemap;
 mod nodemap_docket;
 pub mod path_encode;
-pub use node::{Node, NodeError, NodePrefix, NodePrefixRef};
+pub use node::{FromHexError, Node, NodePrefix, NodePrefixRef};
 pub mod changelog;
 pub mod index;
 pub mod manifest;
--- a/rust/hg-core/src/revlog/node.rs	Mon Jan 25 12:00:23 2021 +0100
+++ b/rust/hg-core/src/revlog/node.rs	Mon Jan 25 12:28:39 2021 +0100
@@ -9,7 +9,7 @@
 //! of a revision.
 
 use bytes_cast::BytesCast;
-use hex::{self, FromHex, FromHexError};
+use hex::{self, FromHex};
 use std::convert::TryFrom;
 use std::fmt;
 
@@ -47,10 +47,9 @@
 /// if they need a loop boundary.
 ///
 /// All methods that create a `Node` either take a type that enforces
-/// the size or fail immediately at runtime with [`ExactLengthRequired`].
+/// the size or return an error at runtime.
 ///
 /// [`nybbles_len`]: #method.nybbles_len
-/// [`ExactLengthRequired`]: struct.NodeError#variant.ExactLengthRequired
 #[derive(Clone, Debug, PartialEq, BytesCast)]
 #[repr(transparent)]
 pub struct Node {
@@ -90,12 +89,8 @@
     }
 }
 
-#[derive(Debug, PartialEq)]
-pub enum NodeError {
-    ExactLengthRequired(usize, String),
-    PrefixTooLong(String),
-    HexError(FromHexError, String),
-}
+#[derive(Debug)]
+pub struct FromHexError;
 
 /// Low level utility function, also for prefixes
 fn get_nybble(s: &[u8], i: usize) -> u8 {
@@ -128,9 +123,9 @@
     ///
     /// To be used in FFI and I/O only, in order to facilitate future
     /// changes of hash format.
-    pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, NodeError> {
+    pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, FromHexError> {
         Ok(NodeData::from_hex(hex.as_ref())
-            .map_err(|e| NodeError::from((e, hex)))?
+            .map_err(|_| FromHexError)?
             .into())
     }
 
@@ -143,19 +138,6 @@
     }
 }
 
-impl<T: AsRef<[u8]>> From<(FromHexError, T)> for NodeError {
-    fn from(err_offender: (FromHexError, T)) -> Self {
-        let (err, offender) = err_offender;
-        let offender = String::from_utf8_lossy(offender.as_ref()).into_owned();
-        match err {
-            FromHexError::InvalidStringLength => {
-                NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, offender)
-            }
-            _ => NodeError::HexError(err, offender),
-        }
-    }
-}
-
 /// The beginning of a binary revision SHA.
 ///
 /// Since it can potentially come from an hexadecimal representation with
@@ -175,31 +157,22 @@
     ///
     /// To be used in FFI and I/O only, in order to facilitate future
     /// changes of hash format.
-    pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, NodeError> {
+    pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, FromHexError> {
         let hex = hex.as_ref();
         let len = hex.len();
         if len > NODE_NYBBLES_LENGTH {
-            return Err(NodeError::PrefixTooLong(
-                String::from_utf8_lossy(hex).to_owned().to_string(),
-            ));
+            return Err(FromHexError);
         }
 
         let is_odd = len % 2 == 1;
         let even_part = if is_odd { &hex[..len - 1] } else { hex };
         let mut buf: Vec<u8> =
-            Vec::from_hex(&even_part).map_err(|e| (e, hex))?;
+            Vec::from_hex(&even_part).map_err(|_| FromHexError)?;
 
         if is_odd {
             let latest_char = char::from(hex[len - 1]);
-            let latest_nybble = latest_char.to_digit(16).ok_or_else(|| {
-                (
-                    FromHexError::InvalidHexCharacter {
-                        c: latest_char,
-                        index: len - 1,
-                    },
-                    hex,
-                )
-            })? as u8;
+            let latest_nybble =
+                latest_char.to_digit(16).ok_or_else(|| FromHexError)? as u8;
             buf.push(latest_nybble << 4);
         }
         Ok(NodePrefix { buf, is_odd })
@@ -329,24 +302,15 @@
 
     #[test]
     fn test_node_from_hex() {
-        assert_eq!(Node::from_hex(&sample_node_hex()), Ok(sample_node()));
+        assert_eq!(Node::from_hex(&sample_node_hex()).unwrap(), sample_node());
 
         let mut short = hex_pad_right("0123");
         short.pop();
         short.pop();
-        assert_eq!(
-            Node::from_hex(&short),
-            Err(NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, short)),
-        );
+        assert!(Node::from_hex(&short).is_err());
 
         let not_hex = hex_pad_right("012... oops");
-        assert_eq!(
-            Node::from_hex(&not_hex),
-            Err(NodeError::HexError(
-                FromHexError::InvalidHexCharacter { c: '.', index: 3 },
-                not_hex,
-            )),
-        );
+        assert!(Node::from_hex(&not_hex).is_err(),);
     }
 
     #[test]
@@ -355,7 +319,7 @@
     }
 
     #[test]
-    fn test_prefix_from_hex() -> Result<(), NodeError> {
+    fn test_prefix_from_hex() -> Result<(), FromHexError> {
         assert_eq!(
             NodePrefix::from_hex("0e1")?,
             NodePrefix {
@@ -386,25 +350,14 @@
 
     #[test]
     fn test_prefix_from_hex_errors() {
-        assert_eq!(
-            NodePrefix::from_hex("testgr"),
-            Err(NodeError::HexError(
-                FromHexError::InvalidHexCharacter { c: 't', index: 0 },
-                "testgr".to_string()
-            ))
-        );
+        assert!(NodePrefix::from_hex("testgr").is_err());
         let mut long = format!("{:x}", NULL_NODE);
         long.push('c');
-        match NodePrefix::from_hex(&long)
-            .expect_err("should be refused as too long")
-        {
-            NodeError::PrefixTooLong(s) => assert_eq!(s, long),
-            err => panic!(format!("Should have been TooLong, got {:?}", err)),
-        }
+        assert!(NodePrefix::from_hex(&long).is_err())
     }
 
     #[test]
-    fn test_is_prefix_of() -> Result<(), NodeError> {
+    fn test_is_prefix_of() -> Result<(), FromHexError> {
         let mut node_data = [0; NODE_BYTES_LENGTH];
         node_data[0] = 0x12;
         node_data[1] = 0xca;
@@ -417,7 +370,7 @@
     }
 
     #[test]
-    fn test_get_nybble() -> Result<(), NodeError> {
+    fn test_get_nybble() -> Result<(), FromHexError> {
         let prefix = NodePrefix::from_hex("dead6789cafe")?;
         assert_eq!(prefix.borrow().get_nybble(0), 13);
         assert_eq!(prefix.borrow().get_nybble(7), 9);
--- a/rust/hg-core/src/revlog/nodemap.rs	Mon Jan 25 12:00:23 2021 +0100
+++ b/rust/hg-core/src/revlog/nodemap.rs	Mon Jan 25 12:28:39 2021 +0100
@@ -13,7 +13,7 @@
 //! is used in a more abstract context.
 
 use super::{
-    node::NULL_NODE, Node, NodeError, NodePrefix, NodePrefixRef, Revision,
+    node::NULL_NODE, FromHexError, Node, NodePrefix, NodePrefixRef, Revision,
     RevlogIndex, NULL_REVISION,
 };
 
@@ -27,14 +27,14 @@
 #[derive(Debug, PartialEq)]
 pub enum NodeMapError {
     MultipleResults,
-    InvalidNodePrefix(NodeError),
+    InvalidNodePrefix,
     /// A `Revision` stored in the nodemap could not be found in the index
     RevisionNotInIndex(Revision),
 }
 
-impl From<NodeError> for NodeMapError {
-    fn from(err: NodeError) -> Self {
-        NodeMapError::InvalidNodePrefix(err)
+impl From<FromHexError> for NodeMapError {
+    fn from(_: FromHexError) -> Self {
+        NodeMapError::InvalidNodePrefix
     }
 }
 
--- a/rust/hg-cpython/src/revlog.rs	Mon Jan 25 12:00:23 2021 +0100
+++ b/rust/hg-cpython/src/revlog.rs	Mon Jan 25 12:28:39 2021 +0100
@@ -18,7 +18,7 @@
 use hg::{
     nodemap::{Block, NodeMapError, NodeTree},
     revlog::{nodemap::NodeMap, RevlogIndex},
-    NodeError, Revision,
+    Revision,
 };
 use std::cell::RefCell;
 
@@ -468,17 +468,12 @@
     match err {
         NodeMapError::MultipleResults => revlog_error(py),
         NodeMapError::RevisionNotInIndex(r) => rev_not_in_index(py, r),
-        NodeMapError::InvalidNodePrefix(s) => invalid_node_prefix(py, &s),
+        NodeMapError::InvalidNodePrefix => {
+            PyErr::new::<ValueError, _>(py, "Invalid node or prefix")
+        }
     }
 }
 
-fn invalid_node_prefix(py: Python, ne: &NodeError) -> PyErr {
-    PyErr::new::<ValueError, _>(
-        py,
-        format!("Invalid node or prefix: {:?}", ne),
-    )
-}
-
 /// Create the module, with __package__ given from parent
 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
     let dotted_name = &format!("{}.revlog", package);