Skip to main content

dyf/
lib.rs

1#![deny(unused_imports)]
2#![deny(missing_docs)]
3//! # Dynamic String Formatting for Rust
4//!
5//! The `dyf` crate brings dynamic string formatting to Rust while supporting the whole variety of string formats available in Rust.
6//! It provides an easy way to implement dynamic formatting for custom types with the implementation of the `DynDisplay` trait.
7//!
8//! ## Features
9//!
10//! - Support for (almost) all standard Rust format specifiers
11//! - Dynamic formatting for custom types via the `DynDisplay` trait
12//! - Macro support for convenient usage
13//! - Support for various standard library types
14//!
15//! ## Usage
16//!
17//! Add the crate to your project:
18//!
19//! ```sh
20//! cargo add dyf
21//! ```
22//!
23//! ### Serde Support
24//!
25//! The `dyf` crate provides optional support for serialization and deserialization using the `serde` crate.
26//! To enable this feature, add the `serde` feature when adding the crate to your project:
27//!
28//! ```sh
29//! cargo add dyf --features serde
30//! ```
31//!
32//! Once the `serde` feature is enabled, the `FormatString` structure derives the `Serialize` and `Deserialize` traits.
33//! This allows you to easily serialize and deserialize `FormatString` instances using the `serde` crate.
34//!
35//! ### Basic Formatting
36//!
37//! ```rust
38//! use dyf::{dformat, FormatString};
39//!
40//! let fmt = FormatString::from_string("Hello, {}!".to_string()).unwrap();
41//! let result = dformat!(&fmt, "world").unwrap();
42//! assert_eq!(result, format!("Hello, {}!", "world"));
43//!
44//! let num_fmt = FormatString::from_string("The answer is: {:>5}".to_string()).unwrap();
45//! let num = 42;
46//! let result = dformat!(&num_fmt, num).unwrap();
47//! assert_eq!(result, format!("The answer is: {:>5}", num));
48//! ```
49//!
50//! ### Advanced Formatting
51//!
52//! ```rust
53//! use dyf::{dformat, FormatString};
54//!
55//! let fmt = FormatString::from_string("{:05} {:<10.2} {:^10}".to_string()).unwrap();
56//! let result = dformat!(&fmt, 42, 42.1234, "hello").unwrap();
57//! assert_eq!(result, format!("{:05} {:<10.2} {:^10}", 42, 42.1234, "hello"));
58//! ```
59//!
60//! ### Custom Type Formatting
61//!
62//! ```rust
63//! use dyf::{DynDisplay, Error, FormatSpec, dformat, FormatString};
64//! use std::fmt::Write;
65//!
66//! struct Point {
67//!     x: i32,
68//!     y: i32,
69//! }
70//!
71//! impl DynDisplay for Point {
72//!     fn dyn_fmt(&self, f: &mut dyf::Formatter<'_>) -> dyf::Result {
73//!         write!(f, "Point({}, {})", self.x, self.y)?;
74//!         Ok(())
75//!     }
76//! }
77//!
78//! impl std::fmt::Display for Point {
79//!     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80//!         write!(f, "Point({}, {})", self.x, self.y)
81//!     }
82//! }
83//!
84//! let point = Point { x: 10, y: 20 };
85//! let fmt = FormatString::from_string("{}".to_string()).unwrap();
86//! let result = dformat!(&fmt, point).unwrap();
87//! assert_eq!(result, format!("{}", point));
88//! ```
89//!
90//! ### Integer Formatting
91//!
92//! ```rust
93//! use dyf::{dformat, FormatString};
94//!
95//! // Decimal formatting
96//! let fmt = FormatString::from_string("{}".to_string()).unwrap();
97//! let result = dformat!(&fmt, 42).unwrap();
98//! assert_eq!(result, format!("{}", 42));
99//!
100//! let fmt = FormatString::from_string("{:5}".to_string()).unwrap();
101//! let result = dformat!(&fmt, 42).unwrap();
102//! assert_eq!(result, format!("{:5}", 42));
103//!
104//! let fmt = FormatString::from_string("{:05}".to_string()).unwrap();
105//! let result = dformat!(&fmt, 42).unwrap();
106//! assert_eq!(result, format!("{:05}", 42));
107//!
108//! let fmt = FormatString::from_string("{:+}".to_string()).unwrap();
109//! let result = dformat!(&fmt, 42).unwrap();
110//! assert_eq!(result, format!("{:+}", 42));
111//!
112//! // Hexadecimal formatting
113//! let fmt = FormatString::from_string("{:x}".to_string()).unwrap();
114//! let result = dformat!(&fmt, 42).unwrap();
115//! assert_eq!(result, format!("{:x}", 42));
116//!
117//! let fmt = FormatString::from_string("{:X}".to_string()).unwrap();
118//! let result = dformat!(&fmt, 42).unwrap();
119//! assert_eq!(result, format!("{:X}", 42));
120//!
121//! // Octal formatting
122//! let fmt = FormatString::from_string("{:o}".to_string()).unwrap();
123//! let result = dformat!(&fmt, 42).unwrap();
124//! assert_eq!(result, format!("{:o}", 42));
125//!
126//! // Binary formatting
127//! let fmt = FormatString::from_string("{:b}".to_string()).unwrap();
128//! let result = dformat!(&fmt, 42).unwrap();
129//! assert_eq!(result, format!("{:b}", 42));
130//! ```
131//!
132//! ### Float Formatting
133//!
134//! ```rust
135//! use dyf::{dformat, FormatString};
136//!
137//! let fmt = FormatString::from_string("{}".to_string()).unwrap();
138//! let result = dformat!(&fmt, 42.0).unwrap();
139//! assert_eq!(result, format!("{}", 42.0));
140//!
141//! let fmt = FormatString::from_string("{:e}".to_string()).unwrap();
142//! let result = dformat!(&fmt, 42.0).unwrap();
143//! assert_eq!(result, format!("{:e}", 42.0));
144//!
145//! let fmt = FormatString::from_string("{:.2}".to_string()).unwrap();
146//! let result = dformat!(&fmt, 42.1234).unwrap();
147//! assert_eq!(result, format!("{:.2}", 42.1234));
148//!
149//! let fmt = FormatString::from_string("{:10.2}".to_string()).unwrap();
150//! let result = dformat!(&fmt, 42.1234).unwrap();
151//! assert_eq!(result, format!("{:10.2}", 42.1234));
152//! ```
153//!
154//! ### String Formatting
155//!
156//! ```rust
157//! use dyf::{dformat, FormatString};
158//!
159//! let fmt = FormatString::from_string("{}".to_string()).unwrap();
160//! let result = dformat!(&fmt, "hello").unwrap();
161//! assert_eq!(result, format!("{}", "hello"));
162//!
163//! let fmt = FormatString::from_string("{:10}".to_string()).unwrap();
164//! let result = dformat!(&fmt, "hello").unwrap();
165//! assert_eq!(result, format!("{:10}", "hello"));
166//!
167//! let fmt = FormatString::from_string("{:.3}".to_string()).unwrap();
168//! let result = dformat!(&fmt, "hello").unwrap();
169//! assert_eq!(result, format!("{:.3}", "hello"));
170//! ```
171//!
172//! ## Supported Format Specifiers
173//!
174//! The crate supports several standard Rust format specifiers, including:
175//!
176//! | Category | Specifiers |
177//! |----------|------------|
178//! | Fill/Alignment | `<` `>` `^` |
179//! | Sign | `+` `-` |
180//! | Alternate | `#` |
181//! | Zero-padding | `0` |
182//! | Width | `{:5}` |
183//! | Precision | `{:.2}` |
184//! | Type | `?` `x` `X` `o` `b` `e` `E` `p` |
185//!
186//! ## Performance Considerations
187//!
188//! The crate is designed with performance in mind. The `FormatString` can be created once and reused multiple times with different arguments.
189//! This is particularly useful in scenarios where the same format string is used repeatedly.
190//!
191//! ```rust
192//! use dyf::{dformat, FormatString};
193//!
194//! let fmt = FormatString::from_string("The value is: {:>10}".to_string()).unwrap();
195//! let result1 = dformat!(&fmt, 42).unwrap();
196//! let result2 = dformat!(&fmt, 100).unwrap();
197//! assert_eq!(result1, format!("The value is: {:>10}", 42));
198//! assert_eq!(result2, format!("The value is: {:>10}", 100));
199//! ```
200//!
201//! ## Limitations
202//!
203//! While this crate aims to support all standard Rust format specifiers, there might be some edge cases that are not yet covered.
204//! If you encounter any unsupported format specifiers or have suggestions for improvements, please open an issue on the GitHub repository.
205//!
206//! ## Contributing
207//!
208//! Contributions are welcome! Please open an issue or submit a pull request on the GitHub repository.
209//!
210//! ## License
211//!
212//! This project is dual-licensed under either:
213//! - GNU General Public License v3.0
214//! - BSD 2-Clause License
215//!
216//! You may choose either license at your option.
217//! See LICENSE-GPL-3 and LICENSE-BSD-2-Clause files for full text.
218
219use std::{
220    borrow::Cow,
221    fmt::{Debug, Display, Write},
222};
223
224use pest::{Parser, iterators::Pair};
225use thiserror::Error;
226
227mod imp;
228mod parser;
229use parser::{FmtParser, Rule};
230
231/// Errors that can occur during dynamic formatting.
232///
233/// This enum represents all possible errors that can occur during the parsing and
234/// application of format specifications.
235#[derive(Debug, Error)]
236pub enum Error {
237    /// An unsupported format specification was encountered.
238    ///
239    /// This error occurs when a format specification contains options or combinations
240    /// that are not supported by the formatting machinery.
241    #[error("unsupported format spec {0}")]
242    UnsupportedSpec(FormatSpec),
243
244    /// The number of arguments doesn't match the number of format specifications.
245    ///
246    /// This error occurs when the number of arguments provided to a format string
247    /// doesn't match the number of format specifications in the string.
248    ///
249    /// # Examples
250    ///
251    /// Providing too few arguments:
252    ///
253    /// ```rust
254    /// use dyf::{FormatString, dformat, Error};
255    ///
256    /// let fmt = FormatString::from_string("{}, {}".to_string()).unwrap();
257    /// let result = dformat!(&fmt, "only one argument");
258    /// assert!(matches!(result, Err(Error::ArgumentCountMismatch(2, 1))));
259    /// ```
260    ///
261    /// Providing too many arguments:
262    ///
263    /// ```rust
264    /// use dyf::{FormatString, dformat, Error};
265    ///
266    /// let fmt = FormatString::from_string("{}".to_string()).unwrap();
267    /// let result = dformat!(&fmt, "one", "extra");
268    /// assert!(matches!(result, Err(Error::ArgumentCountMismatch(1, 2))));
269    /// ```
270    #[error(
271        "number of arguments doesn't match number of format specifications expected={0} found={1}"
272    )]
273    ArgumentCountMismatch(usize, usize),
274
275    /// An error occurred during format string parsing.
276    ///
277    /// This error wraps parsing errors from the underlying [`pest`] parser and provides
278    /// information about syntax errors in format strings.
279    #[error("format parsing error: {0}")]
280    Parse(#[from] Box<pest::error::Error<Rule>>),
281
282    /// A write error occurred while writing formatted output.
283    #[error("format write error")]
284    Write(#[from] std::fmt::Error),
285
286    /// Named or positional argument syntax (`{0}`, `{name}`) is not supported.
287    ///
288    /// Arguments are always matched positionally in the order they are passed.
289    #[error("named/positional arguments are not supported: {0}")]
290    NamedArgument(String),
291}
292
293/// Type alias for `std::result::Result<(), Error>`.
294///
295/// Returned by [`DynDisplay::dyn_fmt`] and the padding helpers on [`Formatter`].
296/// Mirrors [`std::fmt::Result`] in the same way that [`DynDisplay`] mirrors
297/// [`std::fmt::Display`].
298pub type Result = std::result::Result<(), Error>;
299
300/// The per-value formatting context passed to [`DynDisplay::dyn_fmt`].
301///
302/// This type mirrors [`std::fmt::Formatter`]: it bundles the [`FormatSpec`] with
303/// the output sink and exposes convenience methods for writing padded output.
304///
305/// It implements [`std::fmt::Write`], so `write!(f, ...)` works naturally inside
306/// a [`DynDisplay::dyn_fmt`] implementation.
307///
308/// # Examples
309///
310/// Using `write!` directly — padding is applied automatically:
311///
312/// ```
313/// use dyf::{DynDisplay, Formatter};
314/// use std::fmt::Write;
315///
316/// struct Point { x: i32, y: i32 }
317///
318/// impl DynDisplay for Point {
319///     fn dyn_fmt(&self, f: &mut Formatter<'_>) -> dyf::Result {
320///         Ok(write!(f, "Point({}, {})", self.x, self.y)?)
321///     }
322/// }
323/// ```
324pub struct Formatter<'a> {
325    spec: &'a FormatSpec,
326    out: &'a mut dyn Write,
327    default_align: Align,
328    /// Buffer for writes made via the `Write` trait. Flushed with padding by
329    /// [`Formatter::finish`] or emptied (without padding) if `pad`/`pad_integral`
330    /// is called explicitly.
331    buf: String,
332}
333
334impl<'a> Formatter<'a> {
335    /// Creates a new `Formatter` wrapping the given spec and output sink.
336    #[inline]
337    pub fn new(spec: &'a FormatSpec, out: &'a mut dyn Write) -> Self {
338        Self {
339            spec,
340            out,
341            default_align: Align::Left,
342            buf: String::new(),
343        }
344    }
345
346    /// Returns the full [`FormatSpec`].
347    #[inline]
348    pub fn spec(&self) -> &FormatSpec {
349        self.spec
350    }
351
352    /// Returns the fill character (defaults to `' '`).
353    #[inline]
354    pub fn fill(&self) -> char {
355        self.spec.fill.unwrap_or(' ')
356    }
357
358    /// Returns the effective alignment: the value from the format spec if one was
359    /// explicitly requested, otherwise the default set by [`Formatter::set_default_align`].
360    #[inline]
361    pub fn align(&self) -> Align {
362        self.spec.align.unwrap_or(self.default_align)
363    }
364
365    /// Returns the minimum field width, if specified.
366    #[inline]
367    pub fn width(&self) -> Option<usize> {
368        self.spec.width
369    }
370
371    /// Returns the precision, if specified.
372    #[inline]
373    pub fn precision(&self) -> Option<usize> {
374        self.spec.precision
375    }
376
377    /// Returns the sign option, if any.
378    #[inline]
379    pub fn sign(&self) -> Option<Sign> {
380        self.spec.sign
381    }
382
383    /// Returns `true` if the alternate form (`#`) was requested.
384    #[inline]
385    pub fn alternate(&self) -> bool {
386        self.spec.alternate
387    }
388
389    /// Returns `true` if zero-padding (`0`) was requested.
390    #[inline]
391    pub fn zero(&self) -> bool {
392        self.spec.zero
393    }
394
395    /// Returns the format type (e.g. [`FmtType::LowerHex`], [`FmtType::Debug`]).
396    #[inline]
397    pub fn fmt_type(&self) -> FmtType {
398        self.spec.ty
399    }
400
401    /// Sets the default alignment used when none is specified in the format string.
402    ///
403    /// Overrides the initial `Left` default. Call this before `write!(f, ...)` when
404    /// implementing a type that should right-align by default (e.g. a custom numeric type).
405    #[inline]
406    pub fn set_default_align(&mut self, align: Align) {
407        self.default_align = align
408    }
409
410    /// Flushes buffered `write!` content to the output with `Left` as the default
411    /// alignment.
412    ///
413    /// Called automatically by the formatting pipeline after [`DynDisplay::dyn_fmt`]
414    /// returns; there is no need to call this manually.
415    pub(crate) fn finish(&mut self) -> Result {
416        if !self.buf.is_empty() {
417            let buf = std::mem::take(&mut self.buf);
418            self.spec.write_aligned(
419                &buf,
420                self.spec.align.unwrap_or(self.default_align),
421                self.out,
422            )?;
423        }
424        Ok(())
425    }
426}
427
428impl std::fmt::Write for Formatter<'_> {
429    fn write_str(&mut self, s: &str) -> std::fmt::Result {
430        self.buf.push_str(s);
431        Ok(())
432    }
433
434    fn write_char(&mut self, c: char) -> std::fmt::Result {
435        self.buf.push(c);
436        Ok(())
437    }
438}
439
440/// A trait for dynamic display formatting.
441///
442/// This trait provides a way to implement custom formatting for types that need to support
443/// dynamic format specifications at runtime. It's similar to the standard [`std::fmt::Display`] trait
444/// but with additional formatting control through [`FormatSpec`].
445///
446/// # Examples
447///
448/// Basic implementation for a custom type:
449///
450/// ```
451/// use dyf::{DynDisplay, FormatSpec, Error};
452/// use std::fmt::Write;
453///
454/// struct Point {
455///     x: i32,
456///     y: i32,
457/// }
458///
459/// impl DynDisplay for Point {
460///     fn dyn_fmt(&self, f: &mut dyf::Formatter<'_>) -> dyf::Result {
461///         write!(f, "Point({}, {})", self.x, self.y)?;
462///         Ok(())
463///     }
464/// }
465/// ```
466///
467/// Implementation with format-aware behavior:
468///
469/// ```
470/// use dyf::{DynDisplay, FmtType, Error};
471/// use std::fmt::Write;
472///
473/// struct Color {
474///     r: u8,
475///     g: u8,
476///     b: u8,
477/// }
478///
479/// impl DynDisplay for Color {
480///     fn dyn_fmt(&self, f: &mut dyf::Formatter<'_>) -> dyf::Result {
481///         match f.fmt_type() {
482///             FmtType::LowerHex => write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b).map_err(Error::from),
483///             FmtType::UpperHex => write!(f, "#{:02X}{:02X}{:02X}", self.r, self.g, self.b).map_err(Error::from),
484///             FmtType::Debug => write!(f, "Color {{ r: {}, g: {}, b: {} }}", self.r, self.g, self.b).map_err(Error::from),
485///             _ => write!(f, "RGB({}, {}, {})", self.r, self.g, self.b).map_err(Error::from),
486///         }
487///     }
488/// }
489/// ```
490pub trait DynDisplay {
491    /// Formats the value using the given format specification.
492    ///
493    /// # Arguments
494    ///
495    /// * `f` - The formatting context carrying the spec and the output sink
496    ///
497    /// # Errors
498    ///
499    /// Returns an error if the format specification is not supported for this type
500    /// or if writing to the output sink fails.
501    fn dyn_fmt(&self, f: &mut Formatter<'_>) -> Result;
502}
503
504/// Specifies the type of formatting to apply to a value.
505///
506/// The [`FmtType`] enum represents the various format types that can be specified
507/// in a format string after the colon. These determine how values are converted
508/// to strings, including different representations for numbers, debugging output,
509/// and other special formats.
510///
511/// # Format String Representation
512///
513/// In format strings, these types are represented as follows:
514///
515/// | FmtType | Format Specifier | Description |
516/// |---------|------------------|-------------|
517/// | Default | (none) | Default formatting for the type |
518/// | Debug | `?` | Debug representation |
519/// | DebugLowHex | `x?` | Debug representation with lowercase hexadecimal |
520/// | DebugUpHex | `X?` | Debug representation with uppercase hexadecimal |
521/// | LowerHex | `x` | Lowercase hexadecimal |
522/// | UpperHex | `X` | Uppercase hexadecimal |
523/// | Octal | `o` | Octal representation |
524/// | Ptr | `p` | Pointer address |
525/// | Bin | `b` | Binary representation |
526/// | LowExp | `e` | Lowercase exponential notation |
527/// | UpperExp | `E` | Uppercase exponential notation |
528///
529/// # Examples
530///
531/// Basic usage with format specifications:
532///
533/// ```
534/// use dyf::{FmtType, FormatSpec};
535///
536/// // Create a format specification for hexadecimal output
537/// let hex_spec = FormatSpec {
538///     ty: FmtType::LowerHex,
539///     ..Default::default()
540/// };
541///
542/// // Create a format specification for debug output
543/// let debug_spec = FormatSpec {
544///     ty: FmtType::Debug,
545///     ..Default::default()
546/// };
547/// ```
548///
549/// Using with custom formatting:
550///
551/// ```
552/// use dyf::{FmtType, FormatSpec, DynDisplay, Error};
553/// use std::fmt::Write;
554///
555/// struct Color {
556///     r: u8,
557///     g: u8,
558///     b: u8,
559/// }
560///
561/// impl DynDisplay for Color {
562///     fn dyn_fmt(&self, f: &mut dyf::Formatter<'_>) -> dyf::Result {
563///         match f.fmt_type() {
564///             FmtType::LowerHex => Ok(write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?),
565///             FmtType::UpperHex => Ok(write!(f, "#{:02X}{:02X}{:02X}", self.r, self.g, self.b)?),
566///             FmtType::Debug => Ok(write!(f, "Color {{ r: {}, g: {}, b: {} }}", self.r, self.g, self.b)?),
567///             _ => Ok(write!(f, "RGB({}, {}, {})", self.r, self.g, self.b)?),
568///         }
569///     }
570/// }
571/// ```
572#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
573#[derive(Debug, Clone, Copy)]
574pub enum FmtType {
575    /// Default formatting for the type.
576    ///
577    /// This uses the standard display formatting for the type, equivalent to not
578    /// specifying a format type in the format string.
579    Default,
580
581    /// Debug representation.
582    ///
583    /// This uses the debug formatting for the type, equivalent to the `{:?}` format specifier.
584    Debug,
585
586    /// Debug representation with lowercase hexadecimal.
587    ///
588    /// This combines debug formatting with lowercase hexadecimal representation.
589    DebugLowHex,
590
591    /// Debug representation with uppercase hexadecimal.
592    ///
593    /// This combines debug formatting with uppercase hexadecimal representation.
594    DebugUpHex,
595
596    /// Lowercase hexadecimal representation.
597    ///
598    /// This formats numbers in lowercase hexadecimal, equivalent to the `{:x}` format specifier.
599    LowerHex,
600
601    /// Uppercase hexadecimal representation.
602    ///
603    /// This formats numbers in uppercase hexadecimal, equivalent to the `{:X}` format specifier.
604    UpperHex,
605
606    /// Octal representation.
607    ///
608    /// This formats numbers in octal (base-8), equivalent to the `{:o}` format specifier.
609    Octal,
610
611    /// Pointer address representation.
612    ///
613    /// This formats pointer values as memory addresses, equivalent to the `{:p}` format specifier.
614    Ptr,
615
616    /// Binary representation.
617    ///
618    /// This formats numbers in binary (base-2), equivalent to the `{:b}` format specifier.
619    Bin,
620
621    /// Lowercase exponential notation.
622    ///
623    /// This formats floating-point numbers in scientific notation with lowercase 'e',
624    /// equivalent to the `{:e}` format specifier.
625    LowExp,
626
627    /// Uppercase exponential notation.
628    ///
629    /// This formats floating-point numbers in scientific notation with uppercase 'E',
630    /// equivalent to the `{:E}` format specifier.
631    UpperExp,
632}
633
634impl Display for FmtType {
635    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
636        match self {
637            FmtType::Default => Ok(()),
638            FmtType::Debug => write!(f, "?"),
639            FmtType::DebugLowHex => write!(f, "x?"),
640            FmtType::DebugUpHex => write!(f, "X?"),
641            FmtType::LowerHex => write!(f, "x"),
642            FmtType::UpperHex => write!(f, "X"),
643            FmtType::Octal => write!(f, "o"),
644            FmtType::Ptr => write!(f, "p"),
645            FmtType::Bin => write!(f, "b"),
646            FmtType::LowExp => write!(f, "e"),
647            FmtType::UpperExp => write!(f, "E"),
648        }
649    }
650}
651
652/// Specifies the alignment of formatted text within a field width.
653///
654/// The [`Align`] enum determines how text should be aligned when a width is specified
655/// in a format specification. It controls whether the text is left-aligned, right-aligned,
656/// or centered within the allocated space.
657#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
658#[derive(Debug, Clone, Copy)]
659pub enum Align {
660    /// Left-align the text within the field.
661    ///
662    /// When this alignment is used, text is placed at the beginning of the field,
663    /// with any padding added to the right.
664    Left,
665
666    /// Center the text within the field.
667    ///
668    /// When this alignment is used, text is placed in the middle of the field,
669    /// with padding distributed equally on both sides when possible.
670    Center,
671
672    /// Right-align the text within the field.
673    ///
674    /// When this alignment is used, text is placed at the end of the field,
675    /// with any padding added to the left.
676    Right,
677}
678
679impl Display for Align {
680    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
681        match self {
682            Align::Center => write!(f, "^"),
683            Align::Left => write!(f, "<"),
684            Align::Right => write!(f, ">"),
685        }
686    }
687}
688
689/// Specifies how signs should be displayed for numeric values.
690///
691/// The [`Sign`] enum controls the display of signs for numeric values in formatted output.
692/// It determines whether positive numbers should show a plus sign, only negative numbers
693/// should show a minus sign, or no special sign handling should be applied.
694///
695/// # Format Specification
696///
697/// In format strings, these correspond to:
698/// - `+` for `Sign::Positive` (show signs for both positive and negative numbers)
699/// - `-` for `Sign::Negative` (show signs only for negative numbers, default behavior)
700#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
701#[derive(Debug, Clone, Copy)]
702pub enum Sign {
703    /// Always show the sign for numeric values.
704    ///
705    /// Positive numbers will be prefixed with `+`, and negative numbers with `-`.
706    /// This corresponds to the `+` format specifier.
707    Positive,
708
709    /// Only show the sign for negative numbers.
710    ///
711    /// Negative numbers will be prefixed with `-`, while positive numbers will
712    /// have no sign prefix. This is the default behavior and corresponds to
713    /// the `-` format specifier.
714    Negative,
715}
716
717impl Display for Sign {
718    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
719        match self {
720            Sign::Positive => write!(f, "+"),
721            Sign::Negative => write!(f, "-"),
722        }
723    }
724}
725
726/// A complete format specification for dynamic formatting.
727///
728/// [`FormatSpec`] represents all the components of a format specification that can appear
729/// between the colons in a format string: `"{:<5.2}"`. It controls how values are formatted
730/// including alignment, padding, width, precision, and type-specific formatting.
731///
732/// # Format String Components
733///
734/// A format specification in a string typically looks like:
735/// `:[fill][align][sign][#][0][width][.precision][type]`
736#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
737#[derive(Debug, Clone)]
738pub struct FormatSpec {
739    /// The fill character to use for padding.
740    ///
741    /// If `None`, spaces will be used for padding.
742    pub fill: Option<char>,
743
744    /// The alignment of the formatted value within the field.
745    ///
746    /// If `None`, the default alignment (typically right for numbers, left for text) will be used.
747    pub align: Option<Align>,
748
749    /// The sign display option for numeric values.
750    ///
751    /// If `None`, signs will only be shown for negative numbers.
752    pub sign: Option<Sign>,
753
754    /// Whether to use alternate formatting.
755    ///
756    /// For example, adding `0x` prefix to hexadecimal numbers or always showing the decimal point.
757    pub alternate: bool,
758
759    /// Whether to pad with zeros instead of the fill character.
760    ///
761    /// This is typically used for numeric types to ensure a minimum number of digits.
762    pub zero: bool,
763
764    /// The minimum width of the formatted field.
765    ///
766    /// If the formatted value is shorter than this width, it will be padded according to the alignment.
767    pub width: Option<usize>,
768
769    /// The precision for floating-point numbers or maximum length for strings.
770    pub precision: Option<usize>,
771
772    /// The format type specification.
773    pub ty: FmtType,
774}
775
776impl Display for FormatSpec {
777    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
778        if let Some(c) = self.fill {
779            write!(f, "{c}")?;
780        }
781        if let Some(a) = self.align {
782            write!(f, "{a}")?;
783        }
784        if let Some(s) = self.sign {
785            write!(f, "{s}")?;
786        }
787        if self.alternate {
788            write!(f, "#")?;
789        }
790        if self.zero {
791            write!(f, "0")?;
792        }
793        if let Some(w) = self.width {
794            write!(f, "{w}")?;
795        }
796        if let Some(p) = self.precision {
797            write!(f, ".{p}")?;
798        }
799        write!(f, "{}", self.ty)
800    }
801}
802
803impl Default for FormatSpec {
804    fn default() -> Self {
805        Self {
806            fill: None,
807            align: None,
808            sign: None,
809            alternate: false,
810            zero: false,
811            width: None,
812            precision: None,
813            ty: FmtType::Default,
814        }
815    }
816}
817
818impl FormatSpec {
819    fn from_pair(p: Pair<'_, Rule>) -> Self {
820        let mut out = Self::default();
821        for p in p.into_inner() {
822            match p.as_rule() {
823                Rule::fill => out.fill = p.as_str().chars().nth(0),
824                Rule::align => {
825                    out.align = match p.as_str() {
826                        "^" => Some(Align::Center),
827                        "<" => Some(Align::Left),
828                        ">" => Some(Align::Right),
829                        _ => None,
830                    }
831                }
832                Rule::sign => {
833                    out.sign = match p.as_str() {
834                        "-" => Some(Sign::Negative),
835                        "+" => Some(Sign::Positive),
836                        _ => None,
837                    }
838                }
839                Rule::alternate => out.alternate = true,
840                Rule::zero_pad => out.zero = true,
841                Rule::width => out.width = p.as_str().parse::<usize>().ok(),
842                Rule::precision => out.precision = p.as_str().parse::<usize>().ok(),
843                Rule::type_fmt => {
844                    if let Some(type_fmt) = p.into_inner().next() {
845                        match type_fmt.as_rule() {
846                            Rule::type_debug => out.ty = FmtType::Debug,
847                            Rule::type_debug_low_hex => out.ty = FmtType::DebugLowHex,
848                            Rule::type_debug_up_hex => out.ty = FmtType::DebugUpHex,
849                            Rule::type_oct => out.ty = FmtType::Octal,
850                            Rule::type_low_hex => out.ty = FmtType::LowerHex,
851                            Rule::type_up_hex => out.ty = FmtType::UpperHex,
852                            Rule::type_ptr => out.ty = FmtType::Ptr,
853                            Rule::type_bin => out.ty = FmtType::Bin,
854                            Rule::type_low_exp => out.ty = FmtType::LowExp,
855                            Rule::type_upper_exp => out.ty = FmtType::UpperExp,
856                            _ => {}
857                        }
858                    }
859                }
860
861                _ => {}
862            }
863        }
864        out
865    }
866
867    fn is_empty(&self) -> bool {
868        self.fill.is_none()
869            && self.align.is_none()
870            && self.sign.is_none()
871            && !self.alternate
872            && !self.zero
873            && self.width.is_none()
874            && self.precision.is_none()
875            && matches!(self.ty, FmtType::Default)
876    }
877
878    /// Writes `s` into `out`, applying fill and alignment from this format specification.
879    ///
880    /// # Arguments
881    ///
882    /// * `s` - The already-formatted string to align/pad
883    /// * `default_align` - Alignment to use when none is specified in the spec
884    /// * `out` - The output sink to write into
885    pub fn write_aligned(&self, s: &str, default_align: Align, out: &mut dyn Write) -> Result {
886        let Some(width) = self.width else {
887            out.write_str(s)?;
888            return Ok(());
889        };
890        if width <= s.len() {
891            out.write_str(s)?;
892            return Ok(());
893        }
894        let pad = width - s.len();
895        let fill = self.fill.unwrap_or(' ');
896        let align = self.align.unwrap_or(default_align);
897        match align {
898            Align::Left => {
899                out.write_str(s)?;
900                for _ in 0..pad {
901                    out.write_char(fill)?;
902                }
903            }
904            Align::Center => {
905                let r_pad = pad / 2;
906                let l_pad = pad.div_ceil(2);
907                for _ in 0..r_pad {
908                    out.write_char(fill)?;
909                }
910                out.write_str(s)?;
911                for _ in 0..l_pad {
912                    out.write_char(fill)?;
913                }
914            }
915            Align::Right => {
916                for _ in 0..pad {
917                    out.write_char(fill)?;
918                }
919                out.write_str(s)?;
920            }
921        }
922        Ok(())
923    }
924}
925
926#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
927#[derive(Debug, Clone)]
928struct Format {
929    start: usize,
930    end: usize,
931    arg: Option<String>,
932    spec: FormatSpec,
933}
934
935impl Display for Format {
936    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
937        write!(f, "{{")?;
938        if let Some(a) = self.arg.as_ref() {
939            write!(f, "{a}")?;
940        }
941
942        if !self.spec.is_empty() {
943            write!(f, ":")?;
944        }
945        write!(f, "{}", self.spec)?;
946        write!(f, "}}")
947    }
948}
949
950impl Format {
951    fn from_pair(pairs: Pair<'_, Rule>) -> Self {
952        let start = pairs.as_span().start();
953        let end = pairs.as_span().end();
954        let mut spec = None;
955        let mut arg = None;
956
957        for p in pairs.into_inner() {
958            match p.as_rule() {
959                Rule::argument => {
960                    // we ignore it for the moment
961                    arg = Some(p.as_str().to_string())
962                }
963                Rule::format_spec => spec = Some(FormatSpec::from_pair(p)),
964                _ => {}
965            }
966        }
967
968        Self {
969            start,
970            end,
971            arg,
972            spec: spec.unwrap_or_default(),
973        }
974    }
975
976    fn dyn_fmt_arg<D: DynDisplay>(&self, arg: &D, out: &mut dyn Write) -> Result {
977        let mut f = Formatter::new(&self.spec, out);
978        DynDisplay::dyn_fmt(arg, &mut f)?;
979        f.finish()
980    }
981}
982
983/// A parsed format string that can be used for dynamic formatting.
984///
985/// [`FormatString`] represents a string with embedded format specifications that has been
986/// parsed and can be used with the [`dformat!`] macro to perform dynamic formatting operations.
987/// It contains the original string along with information about the format specifications
988/// found within it.
989///
990/// # Examples
991///
992/// Basic usage:
993///
994/// ```
995/// use dyf::FormatString;
996///
997/// let fmt = FormatString::from_string("Hello, {}!".to_string()).unwrap();
998/// assert!(fmt.contains_format());
999/// ```
1000///
1001/// Creating and using a format string:
1002///
1003/// ```
1004/// use dyf::{FormatString, dformat};
1005///
1006/// let fmt = FormatString::from_string("{:>10} {:.2}".to_string()).unwrap();
1007/// let result = dformat!(&fmt, 42, 3.14159).unwrap();
1008/// assert_eq!(result, "        42 3.14");
1009/// ```
1010///
1011/// Converting between string types:
1012///
1013/// ```
1014/// use dyf::FormatString;
1015///
1016/// let fmt = FormatString::from_string("Value: {:05}".to_string()).unwrap();
1017/// let fmt_str = fmt.to_string_lossy();
1018/// let owned_str = fmt.into_string();
1019/// ```
1020#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1021#[derive(Debug, Clone)]
1022pub struct FormatString {
1023    s: String,
1024    fmts: Vec<Format>,
1025}
1026
1027impl FormatString {
1028    #[inline]
1029    fn new_from_str<S: AsRef<str>>(s: S) -> std::result::Result<Self, Error> {
1030        let pairs = FmtParser::parse(Rule::format_string, s.as_ref())
1031            .map_err(|e| Error::from(Box::new(e)))?;
1032
1033        let mut fmts = vec![];
1034
1035        for p in pairs {
1036            // WARNING: here anything else than Rule::format is ignored
1037            if p.as_rule() == Rule::format {
1038                fmts.push(Format::from_pair(p))
1039            }
1040        }
1041
1042        Ok(Self {
1043            s: s.as_ref().to_string(),
1044            fmts,
1045        })
1046    }
1047
1048    /// Creates a new [`FormatString`] from a string.
1049    ///
1050    /// # Arguments
1051    ///
1052    /// * `s` - The string containing format specifications
1053    ///
1054    /// # Returns
1055    ///
1056    /// A `Result` containing the parsed [`FormatString`] or an error if parsing fails.
1057    ///
1058    /// # Errors
1059    ///
1060    /// This function may return an error if the input string contains invalid format
1061    /// specifications that cannot be parsed.
1062    ///
1063    /// # Examples
1064    ///
1065    /// ```
1066    /// use dyf::FormatString;
1067    ///
1068    /// let fmt = FormatString::from_string("Hello, {}!".to_string()).unwrap();
1069    /// ```
1070    pub fn from_string(s: String) -> std::result::Result<Self, Error> {
1071        Self::new_from_str(s)
1072    }
1073
1074    /// Converts the [`FormatString`] into its inner string.
1075    ///
1076    /// This consumes the [`FormatString`] and returns the original string that was used
1077    /// to create it.
1078    ///
1079    /// # Examples
1080    ///
1081    /// ```
1082    /// use dyf::FormatString;
1083    ///
1084    /// let fmt = FormatString::from_string("Test: {}".to_string()).unwrap();
1085    /// let s = fmt.into_string();
1086    /// assert_eq!(s, "Test: {}");
1087    /// ```
1088    pub fn into_string(self) -> String {
1089        self.s
1090    }
1091
1092    /// Returns a borrowed version of the format string.
1093    ///
1094    /// This provides access to the original string without consuming the [`FormatString`].
1095    ///
1096    /// # Examples
1097    ///
1098    /// ```
1099    /// use dyf::FormatString;
1100    ///
1101    /// let fmt = FormatString::from_string("Value: {:.2}".to_string()).unwrap();
1102    /// let borrowed = fmt.to_string_lossy();
1103    /// assert_eq!(&*borrowed, "Value: {:.2}");
1104    /// ```
1105    pub fn to_string_lossy(&self) -> Cow<'_, str> {
1106        Cow::Borrowed(&self.s)
1107    }
1108
1109    /// Returns `true` if the format string contains any format specifications.
1110    ///
1111    /// # Examples
1112    ///
1113    /// ```
1114    /// use dyf::FormatString;
1115    ///
1116    /// let with_fmt = FormatString::from_string("Hello, {}!".to_string()).unwrap();
1117    /// assert!(with_fmt.contains_format());
1118    ///
1119    /// let without_fmt = FormatString::from_string("Hello, world!".to_string()).unwrap();
1120    /// assert!(!without_fmt.contains_format());
1121    /// ```
1122    pub fn contains_format(&self) -> bool {
1123        !self.fmts.is_empty()
1124    }
1125}
1126
1127/// Drives the full formatting pipeline for a [`FormatString`].
1128///
1129/// `Writer` collects arguments via [`Writer::push_arg`] and then writes the
1130/// fully formatted string in one pass with [`Writer::format`].
1131/// Most callers should use the [`dformat!`] macro instead.
1132///
1133/// # Examples
1134///
1135/// Basic usage with a format string:
1136///
1137/// ```
1138/// use dyf::{FormatString, Writer};
1139///
1140/// let fmt = FormatString::from_string("Hello, {}!".to_string()).unwrap();
1141/// let mut w = Writer::from(&fmt);
1142/// w.push_arg(&"world").format().unwrap();
1143/// assert_eq!(w.into_string(), "Hello, world!");
1144/// ```
1145///
1146/// Formatting multiple values:
1147///
1148/// ```
1149/// use dyf::{FormatString, Writer};
1150///
1151/// let fmt = FormatString::from_string("{}, {}!".to_string()).unwrap();
1152/// let mut w = Writer::from(&fmt);
1153/// w.push_arg(&"Hello").push_arg(&"world").format().unwrap();
1154/// assert_eq!(w.into_string(), "Hello, world!");
1155/// ```
1156///
1157/// Using with custom types:
1158///
1159/// ```
1160/// use dyf::{FormatString, Writer, DynDisplay};
1161/// use std::fmt::Write;
1162///
1163/// struct Point { x: i32, y: i32 }
1164///
1165/// impl DynDisplay for Point {
1166///     fn dyn_fmt(&self, f: &mut dyf::Formatter<'_>) -> dyf::Result {
1167///         write!(f, "Point({}, {})", self.x, self.y)?;
1168///         Ok(())
1169///     }
1170/// }
1171///
1172/// let fmt = FormatString::from_string("Point: {}".to_string()).unwrap();
1173/// let point = Point { x: 10, y: 20 };
1174/// let mut w = Writer::from(&fmt);
1175/// w.push_arg(&point).format().unwrap();
1176/// assert_eq!(w.into_string(), "Point: Point(10, 20)");
1177/// ```
1178pub struct Writer<'s> {
1179    /// index in the format string
1180    i: usize,
1181    format_string: &'s FormatString,
1182    args: Vec<&'s dyn DynDisplay>,
1183    out: String,
1184}
1185
1186impl<'s> From<&'s FormatString> for Writer<'s> {
1187    fn from(value: &'s FormatString) -> Self {
1188        Self {
1189            i: 0,
1190            format_string: value,
1191            args: vec![],
1192            out: String::new(),
1193        }
1194    }
1195}
1196
1197impl<'s> Writer<'s> {
1198    /// Adds an argument to be formatted.
1199    ///
1200    /// Arguments are matched to format placeholders in the order they are pushed.
1201    /// Supports method chaining.
1202    ///
1203    /// # Examples
1204    ///
1205    /// ```
1206    /// use dyf::{FormatString, Writer};
1207    ///
1208    /// let fmt = FormatString::from_string("{}, {}!".to_string()).unwrap();
1209    /// let mut w = Writer::from(&fmt);
1210    /// w.push_arg(&"Hello").push_arg(&"world");
1211    /// ```
1212    pub fn push_arg<A>(&mut self, arg: &'s A) -> &mut Self
1213    where
1214        A: DynDisplay,
1215    {
1216        self.args.push(arg);
1217        self
1218    }
1219
1220    /// Applies all format specifications to the collected arguments.
1221    ///
1222    /// Verifies that the argument count matches the number of placeholders, then
1223    /// writes each formatted value into the internal output buffer.
1224    ///
1225    /// # Errors
1226    ///
1227    /// Returns an error if the argument count doesn't match the placeholder count,
1228    /// if a named/positional argument is used, or if a format operation fails.
1229    ///
1230    /// # Examples
1231    ///
1232    /// ```
1233    /// use dyf::{FormatString, Writer};
1234    ///
1235    /// let fmt = FormatString::from_string("{:>5}, {:<5}".to_string()).unwrap();
1236    /// let mut w = Writer::from(&fmt);
1237    /// w.push_arg(&42).push_arg(&"hello").format().unwrap();
1238    /// assert_eq!(w.into_string(), "   42, hello");
1239    /// ```
1240    pub fn format(&mut self) -> std::result::Result<&mut Self, Error> {
1241        if self.args.len() != self.format_string.fmts.len() {
1242            return Err(Error::ArgumentCountMismatch(
1243                // expected
1244                self.format_string.fmts.len(),
1245                // found
1246                self.args.len(),
1247            ));
1248        }
1249
1250        for fmt in &self.format_string.fmts {
1251            if let Some(arg) = &fmt.arg {
1252                return Err(Error::NamedArgument(arg.clone()));
1253            }
1254        }
1255
1256        for (i, a) in self.args.iter().enumerate() {
1257            // this cannot panic as lengths are equal
1258            let arg_fmt = &self.format_string.fmts[i];
1259            let slice = &self.format_string.s.as_str()[self.i..];
1260            self.out.push_str(&slice[..arg_fmt.start - self.i]);
1261            arg_fmt.dyn_fmt_arg(a, &mut self.out)?;
1262            self.i = arg_fmt.end;
1263        }
1264
1265        // we copy the rest of the string
1266        let slice = &self.format_string.s.as_str()[self.i..];
1267        self.out.push_str(slice);
1268
1269        Ok(self)
1270    }
1271
1272    /// Returns a borrowed view of the formatted output so far.
1273    ///
1274    /// # Examples
1275    ///
1276    /// ```
1277    /// use dyf::{FormatString, Writer};
1278    ///
1279    /// let fmt = FormatString::from_string("Value: {}".to_string()).unwrap();
1280    /// let mut w = Writer::from(&fmt);
1281    /// w.push_arg(&42).format().unwrap();
1282    /// assert_eq!(&*w.to_string_lossy(), "Value: 42");
1283    /// ```
1284    pub fn to_string_lossy(&self) -> Cow<'_, str> {
1285        Cow::Borrowed(&self.out)
1286    }
1287
1288    /// Consumes the writer and returns the fully formatted string.
1289    ///
1290    /// # Examples
1291    ///
1292    /// ```
1293    /// use dyf::{FormatString, Writer};
1294    ///
1295    /// let fmt = FormatString::from_string("The answer is: {}".to_string()).unwrap();
1296    /// let mut w = Writer::from(&fmt);
1297    /// w.push_arg(&42).format().unwrap();
1298    /// assert_eq!(w.into_string(), "The answer is: 42");
1299    /// ```
1300    pub fn into_string(self) -> String {
1301        self.out
1302    }
1303}
1304
1305/// Dynamically formats values according to a format string.
1306///
1307/// The `dformat!` macro provides functionality similar to Rust's standard `format!` macro,
1308/// but with dynamic formatting capabilities. It uses a pre-parsed [`FormatString`] to
1309/// apply format specifications to values at runtime.
1310///
1311/// # Syntax
1312///
1313/// The macro takes two arguments:
1314/// - A reference to a [`FormatString`] that has been created from a format string
1315/// - A list of arguments to format
1316///
1317/// ```ignore
1318/// dformat!(format_string_ref, arg1, arg2, ...)
1319/// ```
1320///
1321/// # Examples
1322///
1323/// Basic usage:
1324///
1325/// ```
1326/// use dyf::{FormatString, dformat};
1327///
1328/// let fmt = FormatString::from_string("Hello, {}!".to_string()).unwrap();
1329/// let result = dformat!(&fmt, "world").unwrap();
1330/// assert_eq!(result, "Hello, world!");
1331/// ```
1332///
1333/// Formatting with different specifications:
1334///
1335/// ```
1336/// use dyf::{FormatString, dformat};
1337///
1338/// let fmt = FormatString::from_string("{:>5}, {:.2}".to_string()).unwrap();
1339/// let result = dformat!(&fmt, 42, 3.14159).unwrap();
1340/// assert_eq!(result, "   42, 3.14");
1341/// ```
1342///
1343/// Using with custom types that implement [`DynDisplay`]:
1344///
1345/// ```
1346/// use dyf::{FormatString, dformat, DynDisplay};
1347/// use std::fmt::Write;
1348///
1349/// struct Point {
1350///     x: i32,
1351///     y: i32,
1352/// }
1353///
1354/// impl DynDisplay for Point {
1355///     fn dyn_fmt(&self, f: &mut dyf::Formatter<'_>) -> dyf::Result {
1356///         write!(f, "Point({}, {})", self.x, self.y)?;
1357///         Ok(())
1358///     }
1359/// }
1360///
1361/// let fmt = FormatString::from_string("Point: {}".to_string()).unwrap();
1362/// let point = Point { x: 10, y: 20 };
1363/// let result = dformat!(&fmt, point).unwrap();
1364/// assert_eq!(result, "Point: Point(10, 20)");
1365/// ```
1366///
1367/// # Errors
1368///
1369/// The macro returns a `Result<String, Error>`. Any error might be one
1370/// of the [`enum@Error`] variant.
1371///
1372/// # Performance Considerations
1373///
1374/// For best performance when formatting the same string multiple times:
1375/// 1. Create the `FormatString` once and reuse it
1376/// 2. Use the `dformat!` macro for each formatting operation
1377///
1378/// ```
1379/// use dyf::{FormatString, dformat};
1380///
1381/// let fmt = FormatString::from_string("Value: {:>10}".to_string()).unwrap();
1382/// let result1 = dformat!(&fmt, 42).unwrap();
1383/// let result2 = dformat!(&fmt, "text").unwrap();
1384/// ```
1385///
1386/// # Comparison with Standard `format!` Macro
1387///
1388/// While similar to Rust's standard `format!` macro, `dformat!` provides:
1389/// - Dynamic formatting capabilities through the `DynDisplay` trait
1390/// - The ability to pre-parse format strings for reuse
1391///
1392/// However, for simple cases where you don't need these features, the standard `format!`
1393/// macro is recommended.
1394#[macro_export]
1395macro_rules! dformat {
1396    ($fmt: expr, $($arg: expr),*) => {
1397        {
1398            let mut w = $crate::Writer::from($fmt);
1399            $(
1400                w.push_arg(&$arg);
1401            )*
1402
1403            match w.format() {
1404                Err(e) => Err(e),
1405                Ok(_) => Ok(w.into_string()),
1406            }
1407        }
1408    };
1409}
1410
1411#[cfg(test)]
1412mod tests {
1413    use std::{
1414        ffi::{OsStr, OsString},
1415        net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
1416        path::{Path, PathBuf},
1417        rc::Rc,
1418        sync::Arc,
1419        time::{Duration, Instant, SystemTime},
1420    };
1421
1422    use pest::Parser;
1423
1424    use super::*;
1425
1426    macro_rules! dformat_lit {
1427        ($fmt: literal, $($arg: expr),*) => {
1428            {
1429                let fs = FormatString::new_from_str($fmt).unwrap();
1430                dformat!(&fs, $($arg),*)
1431            }
1432        };
1433    }
1434
1435    #[test]
1436    fn test_rule_format() {
1437        for f in [
1438            "{}", "{0}", "{name}", "{:>5}", "{:<5}", "{:^5}", "{:05}", "{:+}", "{:-}", "{:#b}",
1439            "{:#o}", "{:#x}", "{:.2}", "{:08.2}", "{:x}", "{:X}", "{:o}", "{:b}", "{:e}", "{:E}",
1440            "{:?}", "{:#?}", "{:p}",
1441        ] {
1442            let fmt = Format::from_pair(
1443                FmtParser::parse(Rule::format, f)
1444                    .inspect_err(|e| println!("{e}"))
1445                    .unwrap()
1446                    .next()
1447                    .unwrap(),
1448            );
1449
1450            assert_eq!(f, format!("{fmt}"));
1451        }
1452    }
1453
1454    #[test]
1455    fn test_integer_formatting() {
1456        // Decimal formatting
1457        assert_eq!(format!("{}", 42), dformat_lit!("{}", 42).unwrap());
1458        assert_eq!(format!("{:5}", 42), dformat_lit!("{:5}", 42).unwrap());
1459        assert_eq!(format!("{:05}", 42), dformat_lit!("{:05}", 42).unwrap());
1460        assert_eq!(format!("{:+}", 42), dformat_lit!("{:+}", 42).unwrap());
1461        assert_eq!(format!("{: }", 42), dformat_lit!("{: }", 42).unwrap());
1462        assert_eq!(format!("{:#}", 42), dformat_lit!("{:#}", 42).unwrap());
1463
1464        // Hexadecimal formatting
1465        assert_eq!(format!("{:x}", 42), dformat_lit!("{:x}", 42).unwrap());
1466        assert_eq!(format!("{:X}", 42), dformat_lit!("{:X}", 42).unwrap());
1467        assert_eq!(format!("{:#x}", 42), dformat_lit!("{:#x}", 42).unwrap());
1468        assert_eq!(format!("{:#X}", 42), dformat_lit!("{:#X}", 42).unwrap());
1469
1470        // Octal formatting
1471        assert_eq!(format!("{:o}", 42), dformat_lit!("{:o}", 42).unwrap());
1472        assert_eq!(format!("{:#o}", 42), dformat_lit!("{:#o}", 42).unwrap());
1473
1474        // Binary formatting
1475        assert_eq!(format!("{:b}", 42), dformat_lit!("{:b}", 42).unwrap());
1476        assert_eq!(format!("{:#b}", 42), dformat_lit!("{:#b}", 42).unwrap());
1477
1478        // Width and alignment
1479        assert_eq!(format!("{:5}", 42), dformat_lit!("{:5}", 42).unwrap());
1480        assert_eq!(format!("{:<5}", 42), dformat_lit!("{:<5}", 42).unwrap());
1481        assert_eq!(format!("{:>5}", 42), dformat_lit!("{:>5}", 42).unwrap());
1482        assert_eq!(format!("{:^5}", 42), dformat_lit!("{:^5}", 42).unwrap());
1483        assert_eq!(format!("{:05}", 42), dformat_lit!("{:05}", 42).unwrap());
1484        assert_eq!(format!("{:+<5}", 42), dformat_lit!("{:+<5}", 42).unwrap());
1485        assert_eq!(format!("{:->5}", 42), dformat_lit!("{:->5}", 42).unwrap());
1486        assert_eq!(format!("{:+^5}", 42), dformat_lit!("{:+^5}", 42).unwrap());
1487    }
1488
1489    #[test]
1490    fn test_float_formatting() {
1491        // Float formatting
1492        assert_eq!(format!("{}", 42.0), dformat_lit!("{}", 42.0).unwrap());
1493        assert_eq!(format!("{:e}", 42.0), dformat_lit!("{:e}", 42.0).unwrap());
1494        assert_eq!(format!("{:E}", 42.0), dformat_lit!("{:E}", 42.0).unwrap());
1495        assert_eq!(format!("{:.2}", 42.0), dformat_lit!("{:.2}", 42.0).unwrap());
1496        assert_eq!(
1497            format!("{:.2}", 42.1234),
1498            dformat_lit!("{:.2}", 42.1234).unwrap()
1499        );
1500        assert_eq!(
1501            format!("{:10.2}", 42.1234),
1502            dformat_lit!("{:10.2}", 42.1234).unwrap()
1503        );
1504        assert_eq!(
1505            format!("{:<10.2}", 42.1234),
1506            dformat_lit!("{:<10.2}", 42.1234).unwrap()
1507        );
1508        assert_eq!(
1509            format!("{:>10.2}", 42.1234),
1510            dformat_lit!("{:>10.2}", 42.1234).unwrap()
1511        );
1512        assert_eq!(
1513            format!("{:^10.2}", 42.1234),
1514            dformat_lit!("{:^10.2}", 42.1234).unwrap()
1515        );
1516        assert_eq!(format!("{:+}", 42.0), dformat_lit!("{:+}", 42.0).unwrap());
1517        assert_eq!(
1518            format!("{:+<10.2}", 42.1234),
1519            dformat_lit!("{:+<10.2}", 42.1234).unwrap()
1520        );
1521        assert_eq!(
1522            format!("{:->10.2}", 42.1234),
1523            dformat_lit!("{:->10.2}", 42.1234).unwrap()
1524        );
1525        assert_eq!(
1526            format!("{:+^10.2}", 42.1234),
1527            dformat_lit!("{:+^10.2}", 42.1234).unwrap()
1528        );
1529    }
1530
1531    #[test]
1532    fn test_string_formatting() {
1533        // String formatting
1534        assert_eq!(format!("{}", "hello"), dformat_lit!("{}", "hello").unwrap());
1535        assert_eq!(
1536            format!("Hello, {}!", "world"),
1537            dformat_lit!("Hello, {}!", "world").unwrap()
1538        );
1539        assert_eq!(
1540            format!("{:10}", "hello"),
1541            dformat_lit!("{:10}", "hello").unwrap()
1542        );
1543        assert_eq!(
1544            format!("{:<10}", "hello"),
1545            dformat_lit!("{:<10}", "hello").unwrap()
1546        );
1547        assert_eq!(
1548            format!("{:>10}", "hello"),
1549            dformat_lit!("{:>10}", "hello").unwrap()
1550        );
1551        assert_eq!(
1552            format!("{:^10}", "hello"),
1553            dformat_lit!("{:^10}", "hello").unwrap()
1554        );
1555
1556        // String precision
1557        assert_eq!(
1558            format!("{:.3}", "hello"),
1559            dformat_lit!("{:.3}", "hello").unwrap()
1560        );
1561        assert_eq!(
1562            format!("{:10.3}", "hello"),
1563            dformat_lit!("{:10.3}", "hello").unwrap()
1564        );
1565        assert_eq!(
1566            format!("{:<10.3}", "hello"),
1567            dformat_lit!("{:<10.3}", "hello").unwrap()
1568        );
1569        assert_eq!(
1570            format!("{:>10.3}", "hello"),
1571            dformat_lit!("{:>10.3}", "hello").unwrap()
1572        );
1573        assert_eq!(
1574            format!("{:^10.3}", "hello"),
1575            dformat_lit!("{:^10.3}", "hello").unwrap()
1576        );
1577    }
1578
1579    #[test]
1580    fn test_char_formatting() {
1581        // Character formatting
1582        assert_eq!(format!("{}", 'A'), dformat_lit!("{}", 'A').unwrap());
1583        assert_eq!(format!("{:5}", 'A'), dformat_lit!("{:5}", 'A').unwrap());
1584        assert_eq!(format!("{:<5}", 'A'), dformat_lit!("{:<5}", 'A').unwrap());
1585        assert_eq!(format!("{:>5}", 'A'), dformat_lit!("{:>5}", 'A').unwrap());
1586        assert_eq!(format!("{:^5}", 'A'), dformat_lit!("{:^5}", 'A').unwrap());
1587    }
1588
1589    #[test]
1590    fn test_bool_formatting() {
1591        // Boolean formatting
1592        assert_eq!(format!("{}", true), dformat_lit!("{}", true).unwrap());
1593        assert_eq!(format!("{}", false), dformat_lit!("{}", false).unwrap());
1594        assert_eq!(format!("{:5}", true), dformat_lit!("{:5}", true).unwrap());
1595        assert_eq!(format!("{:<5}", true), dformat_lit!("{:<5}", true).unwrap());
1596        assert_eq!(format!("{:>5}", true), dformat_lit!("{:>5}", true).unwrap());
1597        assert_eq!(format!("{:^5}", true), dformat_lit!("{:^5}", true).unwrap());
1598    }
1599
1600    #[test]
1601    fn test_pointer_formatting() {
1602        // Pointer formatting
1603        let x = 42;
1604        let ptr = &x as *const i32;
1605        assert_eq!(format!("{ptr:p}"), dformat_lit!("{:p}", ptr).unwrap());
1606        assert_eq!(format!("{ptr:10p}"), dformat_lit!("{:10p}", ptr).unwrap());
1607        assert_eq!(format!("{ptr:<10p}"), dformat_lit!("{:<10p}", ptr).unwrap());
1608        assert_eq!(format!("{ptr:>10p}"), dformat_lit!("{:>10p}", ptr).unwrap());
1609        assert_eq!(format!("{ptr:^10p}"), dformat_lit!("{:^10p}", ptr).unwrap());
1610    }
1611
1612    #[test]
1613    fn test_multiple_arguments() {
1614        // Multiple arguments
1615        assert_eq!(
1616            format!("{} {}", "hello", 42),
1617            dformat_lit!("{} {}", "hello", 42).unwrap()
1618        );
1619        assert_eq!(
1620            format!("{:5} {:<10.2}", 42, 42.1234),
1621            dformat_lit!("{:5} {:<10.2}", 42, 42.1234).unwrap()
1622        );
1623        assert_eq!(
1624            format!("{:>5} {:^10.2}", 42, 42.1234),
1625            dformat_lit!("{:>5} {:^10.2}", 42, 42.1234).unwrap()
1626        );
1627    }
1628
1629    #[test]
1630    fn test_complex_formatting() {
1631        // Complex formatting
1632        assert_eq!(
1633            format!("{:05} {:<10.2} {:^10}", 42, 42.1234, "hello"),
1634            dformat_lit!("{:05} {:<10.2} {:^10}", 42, 42.1234, "hello").unwrap()
1635        );
1636        assert_eq!(
1637            format!("{:+<5} {:^10.2} {:>10}", 42, 42.1234, "hello"),
1638            dformat_lit!("{:+<5} {:^10.2} {:>10}", 42, 42.1234, "hello").unwrap()
1639        );
1640    }
1641
1642    #[test]
1643    fn test_network_types() {
1644        // IpAddr, Ipv4Addr, Ipv6Addr
1645        let ipv4_addr = Ipv4Addr::new(127, 0, 0, 1);
1646        let ipv6_addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
1647        let ip_addr_v4 = IpAddr::V4(ipv4_addr);
1648        let ip_addr_v6 = IpAddr::V6(ipv6_addr);
1649
1650        assert_eq!(
1651            format!("{ipv4_addr}"),
1652            dformat_lit!("{}", ipv4_addr).unwrap()
1653        );
1654        assert_eq!(
1655            format!("{ipv4_addr:?}"),
1656            dformat_lit!("{:?}", ipv4_addr).unwrap()
1657        );
1658        assert_eq!(
1659            format!("{ipv6_addr}"),
1660            dformat_lit!("{}", ipv6_addr).unwrap()
1661        );
1662        assert_eq!(
1663            format!("{ipv6_addr:?}"),
1664            dformat_lit!("{:?}", ipv6_addr).unwrap()
1665        );
1666        assert_eq!(
1667            format!("{ip_addr_v4}"),
1668            dformat_lit!("{}", ip_addr_v4).unwrap()
1669        );
1670        assert_eq!(
1671            format!("{ip_addr_v4:?}"),
1672            dformat_lit!("{:?}", ip_addr_v4).unwrap()
1673        );
1674        assert_eq!(
1675            format!("{ip_addr_v6}"),
1676            dformat_lit!("{}", ip_addr_v6).unwrap()
1677        );
1678        assert_eq!(
1679            format!("{ip_addr_v6:?}"),
1680            dformat_lit!("{:?}", ip_addr_v6).unwrap()
1681        );
1682
1683        // SocketAddr, SocketAddrV4, SocketAddrV6
1684        let socket_addr_v4 = SocketAddrV4::new(ipv4_addr, 8080);
1685        let socket_addr_v6 = SocketAddrV6::new(ipv6_addr, 8080, 0, 0);
1686        let socket_addr = SocketAddr::V4(socket_addr_v4);
1687
1688        assert_eq!(
1689            format!("{socket_addr_v4}"),
1690            dformat_lit!("{}", socket_addr_v4).unwrap()
1691        );
1692        assert_eq!(
1693            format!("{socket_addr_v4:?}"),
1694            dformat_lit!("{:?}", socket_addr_v4).unwrap()
1695        );
1696        assert_eq!(
1697            format!("{socket_addr_v6}"),
1698            dformat_lit!("{}", socket_addr_v6).unwrap()
1699        );
1700        assert_eq!(
1701            format!("{socket_addr_v6:?}"),
1702            dformat_lit!("{:?}", socket_addr_v6).unwrap()
1703        );
1704        assert_eq!(
1705            format!("{socket_addr}"),
1706            dformat_lit!("{}", socket_addr).unwrap()
1707        );
1708        assert_eq!(
1709            format!("{socket_addr:?}"),
1710            dformat_lit!("{:?}", socket_addr).unwrap()
1711        );
1712    }
1713
1714    #[test]
1715    fn test_time_types() {
1716        // Duration, SystemTime, Instant
1717        let duration = Duration::from_secs(3600);
1718        let system_time = SystemTime::now();
1719        let instant = Instant::now();
1720
1721        assert_eq!(
1722            format!("{duration:?}"),
1723            dformat_lit!("{:?}", duration).unwrap()
1724        );
1725        assert_eq!(
1726            format!("{system_time:?}"),
1727            dformat_lit!("{:?}", system_time).unwrap()
1728        );
1729        assert_eq!(
1730            format!("{instant:?}"),
1731            dformat_lit!("{:?}", instant).unwrap()
1732        );
1733    }
1734
1735    #[test]
1736    fn test_path_types() {
1737        // Path, PathBuf
1738        let path = Path::new("/path/to/file");
1739        let path_buf = PathBuf::from("/path/to/file");
1740
1741        assert_eq!(format!("{path:?}"), dformat_lit!("{:?}", path).unwrap());
1742        assert_eq!(
1743            format!("{path_buf:?}"),
1744            dformat_lit!("{:?}", path_buf).unwrap()
1745        );
1746    }
1747
1748    #[test]
1749    fn test_ffi_types() {
1750        // OsString, OsStr
1751        let os_string = OsString::from("OS String");
1752        let os_str: &OsStr = os_string.as_os_str();
1753
1754        assert_eq!(
1755            format!("{os_string:?}"),
1756            dformat_lit!("{:?}", os_string).unwrap()
1757        );
1758        assert_eq!(format!("{os_str:?}"), dformat_lit!("{:?}", os_str).unwrap());
1759    }
1760
1761    #[test]
1762    fn test_smart_pointers() {
1763        // Box, Rc, Arc, Cow
1764        let boxed = Box::new(42);
1765        let rc = Rc::new(42);
1766        let arc = Arc::new(42);
1767        let cow_str: Cow<'_, str> = Cow::Borrowed("Hello");
1768
1769        assert_eq!(format!("{boxed}"), dformat_lit!("{}", boxed).unwrap());
1770        assert_eq!(format!("{rc}"), dformat_lit!("{}", rc).unwrap());
1771        assert_eq!(format!("{arc}"), dformat_lit!("{}", arc).unwrap());
1772        assert_eq!(format!("{cow_str}"), dformat_lit!("{}", cow_str).unwrap());
1773    }
1774
1775    #[test]
1776    #[cfg(feature = "serde")]
1777    fn test_serde() {
1778        let fs = FormatString::new_from_str("{}").unwrap();
1779        let js_fs = serde_json::to_string(&fs).unwrap();
1780        let fs: FormatString = serde_json::from_str(&js_fs).unwrap();
1781        assert_eq!(format!("{}", 42), dformat!(&fs, 42).unwrap())
1782    }
1783}