Skip to content

Commit c5c69bd

Browse files
committed
refactor
- fix doc tests and enable them - move all assertions onto integration test level and out of the module - module documentation with credits section. - use rustdoc feature to link other methods - renamed functions to be more obvious
1 parent 3a5a229 commit c5c69bd

File tree

9 files changed

+132
-127
lines changed

9 files changed

+132
-127
lines changed

gix-actor/src/signature/decode.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub(crate) mod function {
22
use bstr::ByteSlice;
33
use gix_date::{time::Sign, OffsetInSeconds, SecondsSinceUnixEpoch, Time};
4-
use gix_utils::btoi::btoi;
4+
use gix_utils::btoi::to_signed;
55
use winnow::{
66
combinator::{alt, separated_pair, terminated},
77
error::{AddContext, ParserError, StrContext},
@@ -23,18 +23,18 @@ pub(crate) mod function {
2323
b" ",
2424
(
2525
terminated(take_until(0.., SPACE), take(1usize))
26-
.verify_map(|v| btoi::<SecondsSinceUnixEpoch>(v).ok())
26+
.verify_map(|v| to_signed::<SecondsSinceUnixEpoch>(v).ok())
2727
.context(StrContext::Expected("<timestamp>".into())),
2828
alt((
2929
take_while(1.., b'-').map(|_| Sign::Minus),
3030
take_while(1.., b'+').map(|_| Sign::Plus),
3131
))
3232
.context(StrContext::Expected("+|-".into())),
3333
take_while(2, AsChar::is_dec_digit)
34-
.verify_map(|v| btoi::<OffsetInSeconds>(v).ok())
34+
.verify_map(|v| to_signed::<OffsetInSeconds>(v).ok())
3535
.context(StrContext::Expected("HH".into())),
3636
take_while(1..=2, AsChar::is_dec_digit)
37-
.verify_map(|v| btoi::<OffsetInSeconds>(v).ok())
37+
.verify_map(|v| to_signed::<OffsetInSeconds>(v).ok())
3838
.context(StrContext::Expected("MM".into())),
3939
)
4040
.map(|(time, sign, hours, minutes)| {

gix-index/src/extension/tree/decode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ fn one_recursive(data: &[u8], hash_len: usize) -> Option<(Tree, &[u8])> {
2222
let (path, data) = split_at_byte_exclusive(data, 0)?;
2323

2424
let (entry_count, data) = split_at_byte_exclusive(data, b' ')?;
25-
let num_entries: i32 = gix_utils::btoi::btoi(entry_count).ok()?;
25+
let num_entries: i32 = gix_utils::btoi::to_signed(entry_count).ok()?;
2626

2727
let (subtree_count, data) = split_at_byte_exclusive(data, b'\n')?;
28-
let subtree_count: usize = gix_utils::btoi::btou(subtree_count).ok()?;
28+
let subtree_count: usize = gix_utils::btoi::to_unsigned(subtree_count).ok()?;
2929

3030
let (id, mut data) = if num_entries >= 0 {
3131
let (hash, data) = split_at_pos(data, hash_len)?;

gix-object/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ pub mod decode {
383383
message: "Did not find 0 byte in header",
384384
})?;
385385
let size_bytes = &input[kind_end + 1..size_end];
386-
let size = gix_utils::btoi::btoi(size_bytes).map_err(|source| ParseIntegerError {
386+
let size = gix_utils::btoi::to_signed(size_bytes).map_err(|source| ParseIntegerError {
387387
source,
388388
message: "Object size in header could not be parsed",
389389
number: size_bytes.into(),

gix-protocol/src/remote_progress.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl<'a> RemoteProgress<'a> {
7575

7676
fn parse_number(i: &mut &[u8]) -> PResult<usize, ()> {
7777
take_till(0.., |c: u8| !c.is_ascii_digit())
78-
.try_map(gix_utils::btoi::btoi)
78+
.try_map(gix_utils::btoi::to_signed)
7979
.parse_next(i)
8080
}
8181

gix-quote/src/ansi_c.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ pub fn undo(input: &BStr) -> Result<(Cow<'_, BStr>, usize), undo::Error> {
8989
})?
9090
.read_exact(&mut buf[1..])
9191
.expect("impossible to fail as numbers match");
92-
let byte =
93-
gix_utils::btoi::btou_radix(&buf, 8).map_err(|e| undo::Error::new(e, original))?;
92+
let byte = gix_utils::btoi::to_unsigned_with_radix(&buf, 8)
93+
.map_err(|e| undo::Error::new(e, original))?;
9494
out.push(byte);
9595
input = &input[2..];
9696
consumed += 2;

gix-utils/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ rust-version = "1.65"
1010
include = ["src/**/*", "LICENSE-*"]
1111

1212
[lib]
13-
doctest = false
13+
doctest = true
1414

1515
[features]
1616
bstr = ["dep:bstr"]

gix-utils/src/btoi.rs

Lines changed: 87 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1-
// ported from https://github.com/niklasf/rust-btoi version 0.4.3
2-
// see https://github.com/Byron/gitoxide/issues/729#issuecomment-1941515655
1+
/// A module with utilities to turn byte slices with decimal numbers back into their
2+
/// binary representation.
3+
///
4+
/// ### Credits
5+
///
6+
/// This module was ported from <https://github.com/niklasf/rust-btoi> version 0.4.3
7+
/// see <https://github.com/Byron/gitoxide/issues/729> for how it came to be in order
8+
/// to save 2.2 seconds of per-core compile time by not compiling the `num-traits` crate
9+
/// anymore.
10+
///
11+
/// Licensed with compatible licenses [MIT] and [Apache]
12+
///
13+
/// [MIT]: https://github.com/niklasf/rust-btoi/blob/master/LICENSE-MIT
14+
/// [Apache]: https://github.com/niklasf/rust-btoi/blob/master/LICENSE-APACHE
315
416
/// An error that can occur when parsing an integer.
517
///
@@ -42,52 +54,6 @@ impl std::error::Error for ParseIntegerError {
4254
self.desc()
4355
}
4456
}
45-
/// minimal subset of traits used by btoi_radix and btou_radix
46-
pub trait MinNumTraits: Sized + Copy {
47-
///
48-
fn from_u32(n: u32) -> Option<Self>;
49-
///
50-
fn zero() -> Self;
51-
///
52-
fn checked_mul(self, v: Self) -> Option<Self>;
53-
///
54-
fn checked_add(self, v: Self) -> Option<Self>;
55-
///
56-
fn checked_sub(self, v: Self) -> Option<Self>;
57-
}
58-
59-
macro_rules! min_num_traits {
60-
($t : ty, from_u32 => $from_u32 : expr) => {
61-
impl MinNumTraits for $t {
62-
fn from_u32(n: u32) -> Option<$t> {
63-
#[allow(clippy::redundant_closure_call)]
64-
$from_u32(n)
65-
}
66-
67-
fn zero() -> Self {
68-
0
69-
}
70-
71-
fn checked_mul(self, v: $t) -> Option<$t> {
72-
<$t>::checked_mul(self, v)
73-
}
74-
75-
fn checked_add(self, v: $t) -> Option<$t> {
76-
<$t>::checked_add(self, v)
77-
}
78-
79-
fn checked_sub(self, v: $t) -> Option<$t> {
80-
<$t>::checked_sub(self, v)
81-
}
82-
}
83-
};
84-
}
85-
86-
min_num_traits!(i32, from_u32 => |n: u32| n.try_into().ok());
87-
min_num_traits!(i64, from_u32 => |n: u32| Some(n.into()));
88-
min_num_traits!(u64, from_u32 => |n: u32| Some(n.into()));
89-
min_num_traits!(u8, from_u32 => |n: u32| n.try_into().ok());
90-
min_num_traits!(usize, from_u32 => |n: u32| n.try_into().ok());
9157

9258
/// Converts a byte slice to an integer. Signs are not allowed.
9359
///
@@ -107,23 +73,14 @@ min_num_traits!(usize, from_u32 => |n: u32| n.try_into().ok());
10773
/// # Examples
10874
///
10975
/// ```
110-
/// # use btoi::btou;
111-
/// assert_eq!(Ok(12345), btou(b"12345"));
112-
/// assert!(btou::<u8>(b"+1").is_err()); // only btoi allows signs
113-
/// assert!(btou::<u8>(b"256").is_err()); // overflow
76+
/// # use gix_utils::btoi::to_unsigned;
77+
/// assert_eq!(Ok(12345), to_unsigned(b"12345"));
78+
/// assert!(to_unsigned::<u8>(b"+1").is_err()); // only btoi allows signs
79+
/// assert!(to_unsigned::<u8>(b"256").is_err()); // overflow
11480
/// ```
115-
///
116-
/// [`ParseIntegerError`]: struct.ParseIntegerError.html
11781
#[track_caller]
118-
pub fn btou<I: MinNumTraits>(bytes: &[u8]) -> Result<I, ParseIntegerError> {
119-
btou_radix(bytes, 10)
120-
}
121-
122-
#[test]
123-
fn btou_assert() {
124-
assert_eq!(Ok(12345), btou(b"12345"));
125-
assert!(btou::<u8>(b"+1").is_err()); // only btoi allows signs
126-
assert!(btou::<u8>(b"256").is_err()); // overflow
82+
pub fn to_unsigned<I: MinNumTraits>(bytes: &[u8]) -> Result<I, ParseIntegerError> {
83+
to_unsigned_with_radix(bytes, 10)
12784
}
12885

12986
/// Converts a byte slice in a given base to an integer. Signs are not allowed.
@@ -145,13 +102,11 @@ fn btou_assert() {
145102
/// # Examples
146103
///
147104
/// ```
148-
/// # use btoi::btou_radix;
149-
/// assert_eq!(Ok(255), btou_radix(b"ff", 16));
150-
/// assert_eq!(Ok(42), btou_radix(b"101010", 2));
105+
/// # use gix_utils::btoi::to_unsigned_with_radix;
106+
/// assert_eq!(Ok(255), to_unsigned_with_radix(b"ff", 16));
107+
/// assert_eq!(Ok(42), to_unsigned_with_radix(b"101010", 2));
151108
/// ```
152-
///
153-
/// [`ParseIntegerError`]: struct.ParseIntegerError.html
154-
pub fn btou_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseIntegerError> {
109+
pub fn to_unsigned_with_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseIntegerError> {
155110
assert!(
156111
(2..=36).contains(&radix),
157112
"radix must lie in the range 2..=36, found {radix}"
@@ -195,15 +150,9 @@ pub fn btou_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseI
195150
Ok(result)
196151
}
197152

198-
#[test]
199-
fn btou_radix_assert() {
200-
assert_eq!(Ok(255), btou_radix(b"ff", 16));
201-
assert_eq!(Ok(42), btou_radix(b"101010", 2));
202-
}
203-
204153
/// Converts a byte slice to an integer.
205154
///
206-
/// Like [`btou`], but numbers may optionally start with a sign (`-` or `+`).
155+
/// Like [`to_unsigned`], but numbers may optionally start with a sign (`-` or `+`).
207156
///
208157
/// # Errors
209158
///
@@ -222,38 +171,23 @@ fn btou_radix_assert() {
222171
/// # Examples
223172
///
224173
/// ```
225-
/// # use btoi::btoi;
226-
/// assert_eq!(Ok(123), btoi(b"123"));
227-
/// assert_eq!(Ok(123), btoi(b"+123"));
228-
/// assert_eq!(Ok(-123), btoi(b"-123"));
174+
/// # use gix_utils::btoi::to_signed;
175+
/// assert_eq!(Ok(123), to_signed(b"123"));
176+
/// assert_eq!(Ok(123), to_signed(b"+123"));
177+
/// assert_eq!(Ok(-123), to_signed(b"-123"));
229178
///
230-
/// assert!(btoi::<i16>(b"123456789").is_err()); // overflow
231-
/// assert!(btoi::<u32>(b"-1").is_err()); // underflow
179+
/// assert!(to_signed::<u8>(b"123456789").is_err()); // overflow
180+
/// assert!(to_signed::<u8>(b"-1").is_err()); // underflow
232181
///
233-
/// assert!(btoi::<i32>(b" 42").is_err()); // leading space
182+
/// assert!(to_signed::<i32>(b" 42").is_err()); // leading space
234183
/// ```
235-
///
236-
/// [`btou`]: fn.btou.html
237-
/// [`ParseIntegerError`]: struct.ParseIntegerError.html
238-
pub fn btoi<I: MinNumTraits>(bytes: &[u8]) -> Result<I, ParseIntegerError> {
239-
btoi_radix(bytes, 10)
240-
}
241-
242-
#[test]
243-
fn btoi_assert() {
244-
assert_eq!(Ok(123), btoi(b"123"));
245-
assert_eq!(Ok(123), btoi(b"+123"));
246-
assert_eq!(Ok(-123), btoi(b"-123"));
247-
248-
assert!(btoi::<u8>(b"123456789").is_err()); // overflow
249-
assert!(btoi::<u64>(b"-1").is_err()); // underflow
250-
251-
assert!(btoi::<i32>(b" 42").is_err()); // leading space
184+
pub fn to_signed<I: MinNumTraits>(bytes: &[u8]) -> Result<I, ParseIntegerError> {
185+
to_signed_with_radix(bytes, 10)
252186
}
253187

254188
/// Converts a byte slice in a given base to an integer.
255189
///
256-
/// Like [`btou_radix`], but numbers may optionally start with a sign
190+
/// Like [`to_unsigned_with_radix`], but numbers may optionally start with a sign
257191
/// (`-` or `+`).
258192
///
259193
/// # Errors
@@ -275,15 +209,12 @@ fn btoi_assert() {
275209
/// # Examples
276210
///
277211
/// ```
278-
/// # use btoi::btoi_radix;
279-
/// assert_eq!(Ok(10), btoi_radix(b"a", 16));
280-
/// assert_eq!(Ok(10), btoi_radix(b"+a", 16));
281-
/// assert_eq!(Ok(-42), btoi_radix(b"-101010", 2));
212+
/// # use gix_utils::btoi::to_signed_with_radix;
213+
/// assert_eq!(Ok(10), to_signed_with_radix(b"a", 16));
214+
/// assert_eq!(Ok(10), to_signed_with_radix(b"+a", 16));
215+
/// assert_eq!(Ok(-42), to_signed_with_radix(b"-101010", 2));
282216
/// ```
283-
///
284-
/// [`btou_radix`]: fn.btou_radix.html
285-
/// [`ParseIntegerError`]: struct.ParseIntegerError.html
286-
fn btoi_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseIntegerError> {
217+
pub fn to_signed_with_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseIntegerError> {
287218
assert!(
288219
(2..=36).contains(&radix),
289220
"radix must lie in the range 2..=36, found {radix}"
@@ -296,9 +227,9 @@ fn btoi_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseInteg
296227
}
297228

298229
let digits = match bytes[0] {
299-
b'+' => return btou_radix(&bytes[1..], radix),
230+
b'+' => return to_unsigned_with_radix(&bytes[1..], radix),
300231
b'-' => &bytes[1..],
301-
_ => return btou_radix(bytes, radix),
232+
_ => return to_unsigned_with_radix(bytes, radix),
302233
};
303234

304235
if digits.is_empty() {
@@ -337,9 +268,49 @@ fn btoi_radix<I: MinNumTraits>(bytes: &[u8], radix: u32) -> Result<I, ParseInteg
337268
Ok(result)
338269
}
339270

340-
#[test]
341-
fn btoi_radix_assert() {
342-
assert_eq!(Ok(10), btoi_radix(b"a", 16));
343-
assert_eq!(Ok(10), btoi_radix(b"+a", 16));
344-
assert_eq!(Ok(-42), btoi_radix(b"-101010", 2));
271+
/// minimal subset of traits used by [`to_signed_with_radix`] and [`to_unsigned_with_radix`]
272+
pub trait MinNumTraits: Sized + Copy {
273+
///
274+
fn from_u32(n: u32) -> Option<Self>;
275+
///
276+
fn zero() -> Self;
277+
///
278+
fn checked_mul(self, v: Self) -> Option<Self>;
279+
///
280+
fn checked_add(self, v: Self) -> Option<Self>;
281+
///
282+
fn checked_sub(self, v: Self) -> Option<Self>;
283+
}
284+
285+
macro_rules! min_num_traits {
286+
($t : ty, from_u32 => $from_u32 : expr) => {
287+
impl MinNumTraits for $t {
288+
fn from_u32(n: u32) -> Option<$t> {
289+
#[allow(clippy::redundant_closure_call)]
290+
$from_u32(n)
291+
}
292+
293+
fn zero() -> Self {
294+
0
295+
}
296+
297+
fn checked_mul(self, v: $t) -> Option<$t> {
298+
<$t>::checked_mul(self, v)
299+
}
300+
301+
fn checked_add(self, v: $t) -> Option<$t> {
302+
<$t>::checked_add(self, v)
303+
}
304+
305+
fn checked_sub(self, v: $t) -> Option<$t> {
306+
<$t>::checked_sub(self, v)
307+
}
308+
}
309+
};
345310
}
311+
312+
min_num_traits!(i32, from_u32 => |n: u32| n.try_into().ok());
313+
min_num_traits!(i64, from_u32 => |n: u32| Some(n.into()));
314+
min_num_traits!(u64, from_u32 => |n: u32| Some(n.into()));
315+
min_num_traits!(u8, from_u32 => |n: u32| n.try_into().ok());
316+
min_num_traits!(usize, from_u32 => |n: u32| n.try_into().ok());

gix-utils/tests/btoi/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use gix_utils::btoi::{to_signed, to_signed_with_radix, to_unsigned, to_unsigned_with_radix};
2+
3+
#[test]
4+
fn binary_to_unsigned() {
5+
assert_eq!(Ok(12345), to_unsigned(b"12345"));
6+
assert!(to_unsigned::<u8>(b"+1").is_err()); // only btoi allows signs
7+
assert!(to_unsigned::<u8>(b"256").is_err()); // overflow
8+
}
9+
10+
#[test]
11+
fn binary_to_unsigned_radix() {
12+
assert_eq!(Ok(255), to_unsigned_with_radix(b"ff", 16));
13+
assert_eq!(Ok(42), to_unsigned_with_radix(b"101010", 2));
14+
}
15+
16+
#[test]
17+
fn binary_to_integer_radix() {
18+
assert_eq!(Ok(10), to_signed_with_radix(b"a", 16));
19+
assert_eq!(Ok(10), to_signed_with_radix(b"+a", 16));
20+
assert_eq!(Ok(-42), to_signed_with_radix(b"-101010", 2));
21+
}
22+
23+
#[test]
24+
fn binary_to_integer() {
25+
assert_eq!(Ok(123), to_signed(b"123"));
26+
assert_eq!(Ok(123), to_signed(b"+123"));
27+
assert_eq!(Ok(-123), to_signed(b"-123"));
28+
29+
assert!(to_signed::<u8>(b"123456789").is_err()); // overflow
30+
assert!(to_signed::<u64>(b"-1").is_err()); // underflow
31+
32+
assert!(to_signed::<i32>(b" 42").is_err()); // leading space
33+
}

gix-utils/tests/utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
mod backoff;
2+
mod btoi;
23
mod buffers;
34
mod str;

0 commit comments

Comments
 (0)