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}