lexical_util/
api.rs

1//! Implement string conversion routines in a single trait.
2
3// NOTE:
4//  We use macros to define the traits, rather than implement here
5//  since we can't define traits for types when both are defined outside
6//  the current crate, including in workspaces.
7
8// FROM LEXICAL
9
10/// Define the [`FromLexical`] trait.
11///
12/// * `name`: The name of the crate calling the function.
13/// * `value`: A numerical value to use for the example.
14/// * `t`: The type of the number for the example.
15/// * `len`: The length of the string form of `value`.
16///
17/// # Examples
18///
19/// ```rust,ignore
20/// from_lexical!("lexical_core", 1234, u64, 4);
21/// ```
22///
23/// [`FromLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexical.html
24#[macro_export]
25#[cfg(any(feature = "parse-floats", feature = "parse-integers"))]
26macro_rules! from_lexical {
27    ($name:literal, $value:literal, $t:ty, $len:literal $(, #[$attr:meta])? $(,)?) => {
28        /// Trait for numerical types that can be parsed from bytes.
29        $(#[$attr])?
30        pub trait FromLexical: lexical_util::num::Number {
31            /// Checked parser for a string-to-number conversion.
32            ///
33            /// This method parses the entire string, returning an error if
34            /// any invalid digits are found during parsing. Returns a [`Result`]
35            /// containing either the parsed value, or an error containing
36            /// any errors that occurred during parsing.
37            ///
38            /// * `bytes`   - Slice containing a numeric string.
39            ///
40            /// # Examples
41            ///
42            /// ```rust
43            #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")]
44            #[doc = concat!("use ", $name, "::FromLexical;")]
45            ///
46            #[doc = concat!("let value = \"", stringify!($value), "\";")]
47            #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical(value.as_bytes());")]
48            #[doc = concat!("assert_eq!(parsed, Ok(", stringify!($value), "));")]
49            /// ```
50            fn from_lexical(bytes: &[u8]) -> lexical_util::result::Result<Self>;
51
52            /// Checked parser for a string-to-number conversion.
53            ///
54            /// This method parses until an invalid digit is found (or the end
55            /// of the string), returning the number of processed digits
56            /// and the parsed value until that point. Returns a [`Result`]
57            /// containing either the parsed value and the number of processed
58            /// digits, or an error containing any errors that occurred during
59            /// parsing.
60            ///
61            /// * `bytes`   - Slice containing a numeric string.
62            ///
63            /// # Examples
64            ///
65            /// ```rust
66            #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")]
67            #[doc = concat!("use ", $name, "::FromLexical;")]
68            ///
69            #[doc = concat!("let value = \"", stringify!($value), "\";")]
70            #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical_partial(value.as_bytes());")]
71            #[doc = concat!("assert_eq!(parsed, Ok((", stringify!($value), ", ", stringify!($len), ")));")]
72            /// ```
73            fn from_lexical_partial(bytes: &[u8]) -> lexical_util::result::Result<(Self, usize)>;
74        }
75    };
76}
77
78/// Define the [`FromLexicalWithOptions`] trait.
79///
80/// * `name`: The name of the crate calling the function.
81/// * `value`: A numerical value to use for the example.
82/// * `t`: The type of the number for the example.
83/// * `len`: The length of the string form of `value`.
84/// * `ops_t`: The options type.
85///
86/// # Examples
87///
88/// ```rust,ignore
89/// from_lexical_with_options!("lexical_core", 1234, u64, 4, ParseIntegerOptions);
90/// ```
91///
92/// [`FromLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexicalWithOptions.html
93#[macro_export]
94#[cfg(any(feature = "parse-floats", feature = "parse-integers"))]
95macro_rules! from_lexical_with_options {
96    ($name:literal, $value:literal, $t:ty, $len:literal, $ops_t:ty $(, #[$attr:meta])? $(,)?) => {
97        /// Trait for numerical types that can be parsed from bytes with custom options.
98        ///
99        /// The [`Options`][Self::Options] type specifies the configurable
100        /// options to provide.
101        $(#[$attr])?
102        pub trait FromLexicalWithOptions: lexical_util::num::Number {
103            /// Custom formatting options for parsing a number.
104            type Options: lexical_util::options::ParseOptions;
105
106            /// Checked parser for a string-to-number conversion.
107            ///
108            /// This method parses the entire string, returning an error if
109            /// any invalid digits are found during parsing. The parsing
110            /// is dictated by the options, which specifies special
111            /// float strings, required float components, digit separators,
112            /// exponent characters, and more. Returns a [`Result`] containing
113            /// either the parsed value, or an error containing any errors
114            /// that occurred during parsing.
115            ///
116            /// * `FORMAT`  - Flags and characters designating the number grammar.
117            /// * `bytes`   - Slice containing a numeric string.
118            /// * `options` - Options to dictate number parsing.
119            ///
120            /// The `FORMAT` packed struct is built using [`NumberFormatBuilder`].
121            /// Any invalid number format will prevent parsing, returning
122            /// the appropriate format error. If you are unsure which format
123            /// to use, use [`STANDARD`].
124            ///
125            /// # Examples
126            ///
127            /// ```rust
128            #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")]
129            #[doc = concat!("use ", $name, "::{format, FromLexicalWithOptions, ", stringify!($ops_t), "};")]
130            ///
131            /// const FORMAT: u128 = format::STANDARD;
132            #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")]
133            #[doc = concat!("let value = \"", stringify!($value), "\";")]
134            #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical_with_options::<FORMAT>(value.as_bytes(), &OPTIONS);")]
135            #[doc = concat!("assert_eq!(parsed, Ok(", stringify!($value), "));")]
136            /// ```
137            ///
138            /// [`NumberFormatBuilder`]: lexical_util::format::NumberFormatBuilder
139            /// [`STANDARD`]: lexical_util::format::STANDARD
140            fn from_lexical_with_options<const FORMAT: u128>(
141                bytes: &[u8],
142                options: &Self::Options,
143            ) -> lexical_util::result::Result<Self>;
144
145            /// Checked parser for a string-to-number conversion.
146            ///
147            /// This method parses until an invalid digit is found (or the end
148            /// of the string), returning the number of processed digits
149            /// and the parsed value until that point. Returns a [`Result`]
150            /// containing either the parsed value and the number of
151            /// processed digits, or an error containing any errors that
152            /// occurred during parsing.
153            ///
154            /// * `FORMAT`  - Flags and characters designating the number grammar.
155            /// * `bytes`   - Slice containing a numeric string.
156            /// * `options` - Options to dictate number parsing.
157            ///
158            /// The `FORMAT` packed struct is built using [`NumberFormatBuilder`].
159            /// Any invalid number format will prevent parsing, returning
160            /// the appropriate format error. If you are unsure which format
161            /// to use, use [`STANDARD`].
162            ///
163            /// # Examples
164            ///
165            /// ```rust
166            #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")]
167            #[doc = concat!("use ", $name, "::{format, FromLexicalWithOptions, ", stringify!($ops_t), "};")]
168            ///
169            /// const FORMAT: u128 = format::STANDARD;
170            #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")]
171            ///
172            #[doc = concat!("let value = \"", stringify!($value), "\";")]
173            #[doc = concat!(
174                "let parsed = ",
175                stringify!($t),
176                "::from_lexical_partial_with_options::<FORMAT>(value.as_bytes(), &OPTIONS);"
177            )]
178            #[doc = concat!("assert_eq!(parsed, Ok((", stringify!($value), ", ", stringify!($len), ")));")]
179            /// ```
180            ///
181            /// [`NumberFormatBuilder`]: lexical_util::format::NumberFormatBuilder
182            /// [`STANDARD`]: lexical_util::format::STANDARD
183            fn from_lexical_partial_with_options<const FORMAT: u128>(
184                bytes: &[u8],
185                options: &Self::Options,
186            ) -> lexical_util::result::Result<(Self, usize)>;
187        }
188    };
189}
190
191// TO LEXICAL
192
193/// Define the [`ToLexical`] trait.
194///
195/// * `name`: The name of the crate calling the function.
196/// * `value`: A numerical value to use for the example.
197/// * `t`: The type of the number for the example.
198///
199/// # Examples
200///
201/// ```rust,ignore
202/// to_lexical!("lexical_core", 1234, u64);
203/// ```
204///
205/// [`ToLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexical.html
206#[macro_export]
207#[cfg(any(feature = "write-floats", feature = "write-integers"))]
208macro_rules! to_lexical {
209    ($name:literal, $value:literal, $t:ty $(, #[$attr:meta])? $(,)?) => {
210        /// Trait for numerical types that can be serialized to bytes.
211        ///
212        /// To determine the number of bytes required to serialize a value to
213        /// string, check the associated constants from a required trait:
214        /// - [`FORMATTED_SIZE`]: The number of bytes required for any number for any
215        ///   radix, that is, `2` to `36`.
216        /// - [`FORMATTED_SIZE_DECIMAL`]: The number of bytes required for decimal (base
217        ///   10) numbers.
218        ///
219        /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE
220        /// [`FORMATTED_SIZE_DECIMAL`]: crate::FormattedSize::FORMATTED_SIZE_DECIMAL
221        $(#[$attr])?
222        pub trait ToLexical:
223            lexical_util::constants::FormattedSize + lexical_util::num::Number
224        {
225            /// Serializer for a number-to-string conversion.
226            ///
227            /// Returns a subslice of the input buffer containing the written bytes,
228            /// starting from the same address in memory as the input slice. That
229            /// is, the `bytes` provided to the function and the returned buffer
230            /// reference the same buffer, just with the number of elements truncated
231            /// to the written digits.
232            ///
233            /// * `value`   - Number to serialize.
234            /// * `bytes`   - Buffer to write number to.
235            ///
236            /// # Examples
237            ///
238            /// ```rust
239            /// use core::str;
240            ///
241            #[doc = concat!("use ", $name, "::{format, FormattedSize, ToLexical};")]
242            ///
243            #[doc = concat!("let value: ", stringify!($t), " = ", stringify!($value), ";")]
244            #[doc = concat!("let mut buffer = [0u8; ", stringify!($t), "::FORMATTED_SIZE_DECIMAL];")]
245            /// let digits = value.to_lexical(&mut buffer);
246            #[doc = concat!("assert_eq!(str::from_utf8(digits), Ok(\"", stringify!($value), "\"));")]
247            /// ```
248            ///
249            /// # Panics
250            ///
251            /// Panics if the buffer is not of sufficient size. The caller
252            /// must provide a slice of sufficient size. In order to ensure
253            /// the function will not panic, ensure the buffer has at least
254            /// [`FORMATTED_SIZE_DECIMAL`] elements.
255            ///
256            /// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
257            fn to_lexical<'a>(self, bytes: &'a mut [u8]) -> &'a mut [u8];
258        }
259    };
260}
261
262/// Define the [`ToLexicalWithOptions`] trait.
263///
264/// * `name`: The name of the crate calling the function.
265/// * `value`: A numerical value to use for the example.
266/// * `t`: The type of the number for the example.
267/// * `ops_t`: The options type.
268///
269/// # Examples
270///
271/// ```rust,ignore
272/// to_lexical_with_options!("lexical_core", 1234, u64, WriteIntegerOptions);
273/// ```
274///
275/// [`ToLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexicalWithOptions.html
276#[macro_export]
277#[cfg(any(feature = "write-floats", feature = "write-integers"))]
278macro_rules! to_lexical_with_options {
279    ($name:literal, $value:literal, $t:ty, $ops_t:ty $(, #[$attr:meta])? $(,)?) => {
280        /// Trait for numerical types that can be serialized to bytes with custom
281        /// options.
282        ///
283        /// To determine the number of bytes required to serialize a value to
284        /// string, check the associated constants from a required trait:
285        /// - [`FORMATTED_SIZE`]: The number of bytes required for any number for any
286        ///   radix, that is, `2` to `36`.
287        /// - [`FORMATTED_SIZE_DECIMAL`]: The number of bytes required for decimal (base
288        ///   10) numbers.
289        ///
290        /// The [`Options`][Self::Options] type specifies the configurable options to provide.
291        ///
292        /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE
293        /// [`FORMATTED_SIZE_DECIMAL`]: crate::FormattedSize::FORMATTED_SIZE_DECIMAL
294        $(#[$attr])?
295        pub trait ToLexicalWithOptions:
296            lexical_util::constants::FormattedSize + lexical_util::num::Number
297        {
298            /// Custom formatting options for writing a number.
299            type Options: lexical_util::options::WriteOptions;
300
301            /// Serializer for a number-to-string conversion.
302            ///
303            /// Returns a subslice of the input buffer containing the written bytes,
304            /// starting from the same address in memory as the input slice. That
305            /// is, the `bytes` provided to the function and the returned buffer
306            /// reference the same buffer, just with the number of elements truncated
307            /// to the written digits.
308            ///
309            /// * `FORMAT`  - Flags and characters designating the number grammar.
310            /// * `value`   - Number to serialize.
311            /// * `bytes`   - Buffer to write number to.
312            /// * `options` - Options for number formatting.
313            ///
314            /// `FORMAT` should be built using [`NumberFormatBuilder`] and includes
315            /// options such as the numerical radix for writing the value to string.
316            /// `options` specificies extra, additional configurations such as
317            /// special values like `NaN` or `+Infinity` for how to serialize
318            /// the number.
319            ///
320            /// [`NumberFormatBuilder`]: crate::NumberFormatBuilder
321            ///
322            /// # Examples
323            ///
324            /// ```rust
325            /// use core::str;
326            ///
327            #[doc = concat!(
328                "use ",
329                $name,
330                "::{format, FormattedSize, ",
331                stringify!($ops_t),
332                ", ToLexicalWithOptions};"
333            )]
334            ///
335            /// const FORMAT: u128 = format::STANDARD;
336            #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")]
337            #[doc = concat!(
338                "const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::<",
339                stringify!($t),
340                ", FORMAT>();"
341            )]
342            ///
343            #[doc = concat!("let value: ", stringify!($t), " = ", stringify!($value), ";")]
344            /// let mut buffer = [0u8; BUFFER_SIZE];
345            /// let digits = value.to_lexical_with_options::<FORMAT>(&mut buffer, &OPTIONS);
346            #[doc = concat!("assert_eq!(str::from_utf8(digits), Ok(\"", stringify!($value), "\"));")]
347            /// ```
348            ///
349            /// # Panics
350            ///
351            /// Panics if the buffer is not of sufficient size. The caller
352            /// must provide a slice of sufficient size. In order to ensure
353            /// the function will not panic, ensure the buffer has at least
354            /// [`Options::buffer_size_const`] elements. This is required
355            /// only when changing the number of significant digits, the
356            /// exponent break point, or disabling scientific notation.
357            ///
358            /// If you are not using [`min_significant_digits`] (floats only),
359            /// 1200 bytes is always enough to hold the the output for a custom
360            /// radix, and `400` is always enough for decimal strings.
361            ///
362            /// **Floats Only**
363            ///
364            /// These panics are only when using uncommon features for float
365            /// writing, represent configuration errors, so runtime error
366            /// handling is not provided.
367            ///
368            /// Also panics if the provided number format is invalid, or
369            /// if the mantissa radix is not equal to the exponent base
370            /// and the mantissa radix/exponent base combinations are
371            /// not in the following list:
372            ///
373            /// - `4, 2`
374            /// - `8, 2`
375            /// - `16, 2`
376            /// - `32, 2`
377            /// - `16, 4`
378            ///
379            /// Panics as well if `the` NaN or `Inf` string provided to the writer
380            /// is disabled, but the value provided is `NaN` or `Inf`, respectively.
381            ///
382            #[doc = concat!(
383                "[`Options::buffer_size_const`]: crate::",
384                stringify!($ops_t),
385                "::buffer_size_const"
386            )]
387            /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE
388            /// [`min_significant_digits`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.min_significant_digits
389            fn to_lexical_with_options<'a, const FORMAT: u128>(
390                self,
391                bytes: &'a mut [u8],
392                options: &Self::Options,
393            ) -> &'a mut [u8];
394        }
395    };
396}