num_parse/
parseint.rs

1//! JavaScript-style parseInt-like parsing of numbers from strings in Rust.
2use super::*;
3use num;
4
5/// Internal function to parse uint values from a char-iterator with a given radix.
6fn parse_uint_internal<T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive>(
7    chars: &mut dyn PeekableIterator<Item = char>,
8    mut radix: Option<u32>,
9) -> Option<T> {
10    let mut ret = T::zero();
11    let mut any = false;
12
13    if radix.is_none() {
14        if let Some('0') = chars.peek() {
15            chars.next();
16            any = true;
17
18            match chars.peek() {
19                Some('x') | Some('X') => {
20                    radix = Some(16);
21                    chars.next();
22                    any = false; // expecting a hex number!
23                }
24                _ => {}
25            }
26        }
27    }
28
29    let radix = radix.unwrap_or(10);
30
31    while let Some(dig) = chars.peek() {
32        match dig.to_digit(radix) {
33            Some(digit) => {
34                ret = ret.checked_mul(&T::from_u32(radix).unwrap()).unwrap();
35                ret = ret.checked_add(&T::from_u32(digit).unwrap()).unwrap();
36
37                chars.next();
38                any = true;
39            }
40            None => break,
41        }
42    }
43
44    if any {
45        Some(ret)
46    } else {
47        None
48    }
49}
50
51/// Parse uint values from an iterator with a given radix.
52pub fn parse_uint_from_iter_with_radix<
53    T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive,
54>(
55    chars: &mut dyn PeekableIterator<Item = char>,
56    radix: Option<u32>,
57    whitespace: bool,
58) -> Option<T> {
59    while let Some(ch) = chars.peek() {
60        if whitespace && ch.is_whitespace() {
61            chars.next();
62            continue;
63        } else if *ch == '+' {
64            chars.next();
65        }
66
67        break;
68    }
69
70    parse_uint_internal::<T>(chars, radix)
71}
72
73/// Parse decimal uint values from an iterator.
74pub fn parse_uint_from_iter<
75    T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive,
76>(
77    chars: &mut dyn PeekableIterator<Item = char>,
78    whitespace: bool,
79) -> Option<T> {
80    parse_uint_from_iter_with_radix(chars, None, whitespace)
81}
82
83/// Parse int values from an iterator with a given radix.
84pub fn parse_int_from_iter_with_radix<
85    T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive + num::Signed,
86>(
87    chars: &mut dyn PeekableIterator<Item = char>,
88    radix: Option<u32>,
89    whitespace: bool,
90) -> Option<T> {
91    let mut neg = false;
92
93    while let Some(ch) = chars.peek() {
94        if whitespace && ch.is_whitespace() {
95            chars.next();
96            continue;
97        }
98
99        if *ch == '+' || *ch == '-' {
100            neg = *ch == '-';
101            chars.next();
102        }
103
104        break;
105    }
106
107    if let Some(ret) = parse_uint_internal::<T>(chars, radix) {
108        if neg {
109            Some(-ret)
110        } else {
111            Some(ret)
112        }
113    } else {
114        None
115    }
116}
117
118/// Parse decimal int values from an iterator.
119pub fn parse_int_from_iter<
120    T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive + num::Signed,
121>(
122    chars: &mut dyn PeekableIterator<Item = char>,
123    whitespace: bool,
124) -> Option<T> {
125    parse_int_from_iter_with_radix::<T>(chars, None, whitespace)
126}
127
128/// Parse uint values from a &str with a given radix.
129pub fn parse_uint_with_radix<
130    T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive,
131>(
132    s: &str,
133    radix: u32,
134) -> Option<T> {
135    parse_uint_from_iter_with_radix::<T>(&mut s.chars().peekable(), Some(radix), true)
136}
137
138/// Parse decimal uint values from a &str.
139pub fn parse_uint<T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive>(
140    s: &str,
141) -> Option<T> {
142    parse_uint_from_iter_with_radix::<T>(&mut s.chars().peekable(), None, true)
143}
144
145/// Parse int values from a &str with a given radix.
146pub fn parse_int_with_radix<
147    T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive + num::Signed,
148>(
149    s: &str,
150    radix: u32,
151) -> Option<T> {
152    parse_int_from_iter_with_radix::<T>(&mut s.chars().peekable(), Some(radix), true)
153}
154
155/// Parse decimal int values from a &str.
156pub fn parse_int<
157    T: num::Integer + num::CheckedAdd + num::CheckedMul + num::FromPrimitive + num::Signed,
158>(
159    s: &str,
160) -> Option<T> {
161    parse_int_from_iter_with_radix::<T>(&mut s.chars().peekable(), None, true)
162}
163
164#[test]
165fn test_parse_uint_i64() {
166    assert_eq!(parse_uint::<i64>(" 123hello "), Some(123i64));
167    assert_eq!(parse_uint::<i64>(" 0xcafebabe "), Some(3405691582i64));
168    assert_eq!(parse_uint::<i64>(" 0 "), Some(0));
169    assert_eq!(parse_uint::<i64>(" 0x "), None);
170    assert_eq!(parse_uint::<i64>(" 0x1 "), Some(1));
171    assert_eq!(parse_uint::<i64>(" 456hello "), Some(456i64));
172    assert_eq!(parse_uint::<i64>(" -789hello "), None);
173}
174
175#[test]
176fn test_parse_uint_base16_i64() {
177    assert_eq!(
178        parse_uint_with_radix::<i64>("CAFEBABE", 16),
179        Some(3405691582i64)
180    );
181    assert_eq!(
182        parse_uint_with_radix::<i64>("  cafebabeyeah", 16),
183        Some(3405691582i64)
184    );
185    assert_eq!(parse_int_with_radix::<i64>("  0xcafebabeyeah", 16), Some(0));
186}
187
188#[test]
189fn test_parse_int_i64() {
190    assert_eq!(parse_int::<i64>("123hello"), Some(123i64));
191    assert_eq!(parse_int::<i64>("    456hello"), Some(456i64));
192    assert_eq!(parse_int::<i64>("  -789hello"), Some(-789i64));
193}
194
195#[test]
196fn test_parse_int_base16_i64() {
197    assert_eq!(
198        parse_int_with_radix::<i64>("  -CAFEBABE", 16),
199        Some(-3405691582i64)
200    );
201    assert_eq!(
202        parse_int_with_radix::<i64>("  -cafebabeyeah", 16),
203        Some(-3405691582i64)
204    );
205    assert_eq!(
206        parse_int_with_radix::<i64>("  -0xcafebabeyeah", 16),
207        Some(0)
208    );
209}
210
211#[test]
212fn test_readme() {
213    assert_eq!(parse_uint::<i32>("+123 as i32 "), Some(123i32));
214    assert_eq!(parse_int::<i32>(" -123 as i32 "), Some(-123i32));
215    assert_eq!(parse_uint::<i64>("+123 as i64 "), Some(123i64));
216    assert_eq!(parse_int::<i64>(" -123 as i64 "), Some(-123i64));
217
218    assert_eq!(parse_int::<i64>(" - 1 is invalid "), None);
219    assert_eq!(
220        parse_uint::<u64>(" -123 as u64, parse_int() not available for this type "),
221        None
222    );
223    assert_eq!(
224        parse_uint::<usize>(" 0xcafebabe triggers hex-mode parsing "),
225        Some(3405691582usize)
226    );
227}