diff --git a/rust/hg-core/src/dirstate/dirs_multiset.rs b/rust/hg-core/src/dirstate/dirs_multiset.rs --- a/rust/hg-core/src/dirstate/dirs_multiset.rs +++ b/rust/hg-core/src/dirstate/dirs_multiset.rs @@ -108,7 +108,7 @@ Entry::Vacant(_) => { return Err(DirstateMapError::PathNotFound( path.as_ref().to_owned(), - )) + )); } }; } diff --git a/rust/hg-core/src/matchers.rs b/rust/hg-core/src/matchers.rs --- a/rust/hg-core/src/matchers.rs +++ b/rust/hg-core/src/matchers.rs @@ -81,7 +81,10 @@ /// Matches everything. ///``` -/// use hg::{ matchers::{Matcher, AlwaysMatcher}, utils::hg_path::HgPath }; +/// use hg::{ +/// matchers::{AlwaysMatcher, Matcher}, +/// utils::hg_path::HgPath, +/// }; /// /// let matcher = AlwaysMatcher; /// @@ -121,7 +124,10 @@ /// patterns. /// ///``` -/// use hg::{ matchers::{Matcher, FileMatcher}, utils::hg_path::HgPath }; +/// use hg::{ +/// matchers::{FileMatcher, Matcher}, +/// utils::hg_path::HgPath, +/// }; /// /// let files = [HgPath::new(b"a.txt"), HgPath::new(br"re:.*\.c$")]; /// let matcher = FileMatcher::new(&files).unwrap(); diff --git a/rust/hg-core/src/revlog.rs b/rust/hg-core/src/revlog.rs --- a/rust/hg-core/src/revlog.rs +++ b/rust/hg-core/src/revlog.rs @@ -5,7 +5,9 @@ // GNU General Public License version 2 or any later version. //! Mercurial concepts for handling revision history +pub mod node; pub mod nodemap; +pub use node::{Node, NodeError}; /// Mercurial revision numbers /// diff --git a/rust/hg-core/src/revlog/node.rs b/rust/hg-core/src/revlog/node.rs new file mode 100644 --- /dev/null +++ b/rust/hg-core/src/revlog/node.rs @@ -0,0 +1,91 @@ +// Copyright 2019-2020 Georges Racinet +// +// This software may be used and distributed according to the terms of the +// GNU General Public License version 2 or any later version. + +//! Definitions and utilities for Revision nodes +//! +//! In Mercurial code base, it is customary to call "a node" the binary SHA +//! of a revision. + +use std::num::ParseIntError; + +/// Binary revisions SHA +pub type Node = [u8; 20]; + +/// The node value for NULL_REVISION +pub const NULL_NODE: Node = [0; 20]; + +#[derive(Debug, PartialEq)] +pub enum NodeError { + ExactLengthRequired(String), + NotHexadecimal, +} + +pub fn node_from_hex(hex: &str) -> Result { + if hex.len() != 40 { + return Err(NodeError::ExactLengthRequired(hex.to_string())); + } + let mut node = [0; 20]; + for i in 0..20 { + node[i] = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16)? + } + Ok(node) +} + +pub fn node_to_hex(n: &Node) -> String { + let as_vec: Vec = n.iter().map(|b| format!("{:02x}", b)).collect(); + as_vec.join("") +} + +/// Retrieve the `i`th half-byte from a bytes slice +/// +/// This is also the `i`th hexadecimal digit in numeric form, +/// also called a [nybble](https://en.wikipedia.org/wiki/Nibble). +pub fn get_nybble(i: usize, s: &[u8]) -> u8 { + if i % 2 == 0 { + s[i / 2] >> 4 + } else { + s[i / 2] & 0x0f + } +} + +impl From for NodeError { + fn from(_: ParseIntError) -> Self { + NodeError::NotHexadecimal + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE_NODE_HEX: &str = "0123456789abcdeffedcba9876543210deadbeef"; + + #[test] + fn test_node_from_hex() { + assert_eq!( + node_from_hex(SAMPLE_NODE_HEX), + Ok([ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, + 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef + ]) + ); + let short = "0123456789abcdeffedcba9876543210"; + assert_eq!( + node_from_hex(short), + Err(NodeError::ExactLengthRequired(short.to_string())), + ); + } + + #[test] + fn test_node_to_hex() { + assert_eq!( + node_to_hex(&[ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, + 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef + ]), + SAMPLE_NODE_HEX + ); + } +} diff --git a/rust/hg-core/src/utils.rs b/rust/hg-core/src/utils.rs --- a/rust/hg-core/src/utils.rs +++ b/rust/hg-core/src/utils.rs @@ -18,10 +18,7 @@ /// use crate::hg::utils::replace_slice; /// let mut line = b"I hate writing tests!".to_vec(); /// replace_slice(&mut line, b"hate", b"love"); -/// assert_eq!( -/// line, -/// b"I love writing tests!".to_vec() -/// ); +/// assert_eq!(line, b"I love writing tests!".to_vec()); /// ``` pub fn replace_slice(buf: &mut [T], from: &[T], to: &[T]) where @@ -66,18 +63,9 @@ /// ``` /// use hg::utils::SliceExt; - /// assert_eq!( - /// b" to trim ".trim(), - /// b"to trim" - /// ); - /// assert_eq!( - /// b"to trim ".trim(), - /// b"to trim" - /// ); - /// assert_eq!( - /// b" to trim".trim(), - /// b"to trim" - /// ); + /// assert_eq!(b" to trim ".trim(), b"to trim"); + /// assert_eq!(b"to trim ".trim(), b"to trim"); + /// assert_eq!(b" to trim".trim(), b"to trim"); /// ``` fn trim(&self) -> &[u8] { self.trim_start().trim_end() diff --git a/rust/hg-core/src/utils/hg_path.rs b/rust/hg-core/src/utils/hg_path.rs --- a/rust/hg-core/src/utils/hg_path.rs +++ b/rust/hg-core/src/utils/hg_path.rs @@ -157,7 +157,7 @@ return Err(HgPathError::ContainsNullByte( bytes.to_vec(), index, - )) + )); } b'/' => { if previous_byte.is_some() && previous_byte == Some(b'/') {