num_literal_traits/lib.rs
1// Copyright 2025 Sascha Klick
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Parsing common formats numeric literal formats
10//!
11//! ## Compatibility
12//!
13//! The `num-literal-traits` crate is tested for rustc 1.88 and greater.
14use num_traits::Num;
15
16/// The trait adds String parsing functions to types already implementing
17/// the num_traits::Num trait.
18pub trait NumLiteralTrait<T: Num>: Num {
19
20 /// Determine the literal type, then convert to a number value or
21 /// return an error.
22 ///
23 /// # Arguments
24 /// - `text`: Textual representation of a number.
25 /// # Returns
26 /// - Numerical result or error.
27 ///
28 /// # Examples
29 ///
30 /// ```rust
31 /// use num_traits::Num;
32 /// use num_literal_traits::NumLiteralTrait;
33 ///
34 /// let result = u32::parse_literal("0xCAFE");
35 /// assert_eq!(result, Ok(0xcafe));
36 ///
37 /// let result = u32::parse_literal("0b1000_0001_1111_1010");
38 /// assert_eq!(result, Ok(33274));
39 ///
40 /// let result = u32::parse_literal("CAFE");
41 /// assert!(result.is_err());
42 ///
43 /// let result = u32::parse_literal("'A'");
44 /// assert_eq!(result, Ok(65));
45 /// ```
46 ///
47 /// # Supported formats
48 ///
49 /// Most integer literal formats found in C and C++ are supported:
50 /// Binary : `0b100010`, `0B0`, `0b10101101`
51 /// Octal : `0123`, `00`, `04763523`
52 /// Decimal : `123`, `0`, `7635223`
53 /// Hexadecimal : `0xCAFE`, `0x0`, `0xa1fb484`
54 /// Char : `'A'`, `'!'`
55 ///
56 /// Additionally, the numeric parts can contain underscores `_` to
57 /// which get removed before converting.
58 fn parse_literal(text: &str) -> Result<T, T::FromStrRadixErr>;
59
60 /// Determine the literal type, then convert to a number value or
61 /// return the provided fallback if the parsing fails.
62 ///
63 /// # Arguments
64 /// - `text`: Textual representation of a number.
65 /// - `fallback`: Fallback return value if the parsing fails.
66 /// # Returns
67 /// - Numerical result, either the parsed value or the fallback.
68 ///
69 /// # Examples
70 ///
71 /// ```rust
72 /// use num_traits::Num;
73 /// use num_literal_traits::NumLiteralTrait;
74 ///
75 /// let result = u32::parse_literal_fallback("0xCAFE", 0xfabc);
76 /// assert_eq!(result, 0xcafe);
77 ///
78 /// let result = u32::parse_literal_fallback("CAFE", 0xfabc);
79 /// assert_eq!(result, 0xfabc);
80 ///
81 /// let result = u32::parse_literal_fallback("'全'", 0xfabc);
82 /// assert_eq!(result, 0xfabc);
83 ///
84 /// ```
85 fn parse_literal_fallback(text: &str, fallback: T) -> T;
86}
87
88mod parse_literal;
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95
96 fn null_works() {
97 let result = u32::parse_literal("0");
98 assert_eq!(result, Ok(0));
99 }
100 fn binary_works() {
101 let result = u32::parse_literal("0B1000111011000010");
102 assert_eq!(result, Ok(36546));
103 }
104
105 #[test]
106 fn octal_works() {
107 let result = u32::parse_literal("0723642");
108 assert_eq!(result, Ok(239522));
109 }
110
111 #[test]
112 fn decimal_works() {
113 let result = u32::parse_literal("9823642");
114 assert_eq!(result, Ok(9823642));
115 }
116
117 #[test]
118 fn hexadecimal_works() {
119 let result = u32::parse_literal("0xA82c6fE");
120 assert_eq!(result, Ok(0xa82c6fe));
121 }
122
123 #[test]
124 fn fallback_works() {
125 let result = u32::parse_literal_fallback("random text", 0xCAFE);
126 assert_eq!(result, 0xcafe);
127 }
128
129 #[test]
130 fn underscores_works() {
131 let result = u32::parse_literal("0b1101_0011_1000_0111");
132 assert_eq!(result, Ok(54151));
133 }
134
135 #[test]
136 fn random_text_fails() {
137 let res = u32::parse_literal("t322545");
138 assert!(res.is_err());
139 }
140
141 #[test]
142 fn empty_char_fails() {
143 let res = u32::parse_literal("''");
144 assert!(res.is_err());
145 }
146
147 #[test]
148 fn multiple_chars_fails() {
149 let res = u32::parse_literal("'ABC'");
150 assert!(res.is_err());
151 }
152
153 #[test]
154 fn nonascii_chars_fails() {
155 let res = u32::parse_literal("'全'");
156 assert!(res.is_err());
157 }
158
159}