pw_format/lib.rs
1// Copyright 2023 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15//! The `pw_format` crate provides format string parsing and formatting utilities to:
16//! * Parse and syntax-check format strings (`printf` and `core::fmt`).
17//! * Understand format string argument types at compile time in proc macros.
18//! * Format dynamic values according to format strings at runtime.
19//!
20//! `pw_format` is written against `std` and is not intended to be
21//! used in an embedded on-device context. Some efficiency and memory is traded for a
22//! more expressive interface that exposes the format string's syntax tree
23//! to the API client.
24//!
25//! # Proc Macros
26//!
27//! The `macros` module provides infrastructure for implementing proc macros
28//! that take format strings as arguments.
29//!
30//! # Parsing Example
31//!
32//! ```
33//! use pw_format::{
34//! Alignment, Argument, ConversionSpec, Flag, FormatFragment, FormatString,
35//! Length, MinFieldWidth, Precision, Primitive, Style,
36//! };
37//!
38//! let format_string =
39//! FormatString::parse_printf("long double %+ 4.2Lf is %-03hd%%.").unwrap();
40//!
41//! assert_eq!(format_string, FormatString {
42//! fragments: vec![
43//! FormatFragment::Literal("long double ".to_string()),
44//! FormatFragment::Conversion(ConversionSpec {
45//! argument: Argument::None,
46//! fill: ' ',
47//! alignment: Alignment::None,
48//! flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
49//! min_field_width: MinFieldWidth::Fixed(4),
50//! precision: Precision::Fixed(2),
51//! length: Some(Length::LongDouble),
52//! primitive: Primitive::Float,
53//! style: Style::None,
54//! }),
55//! FormatFragment::Literal(" is ".to_string()),
56//! FormatFragment::Conversion(ConversionSpec {
57//! argument: Argument::None,
58//! fill: ' ',
59//! alignment: Alignment::Left,
60//! flags: [Flag::LeftJustify, Flag::LeadingZeros]
61//! .into_iter()
62//! .collect(),
63//! min_field_width: MinFieldWidth::Fixed(3),
64//! precision: Precision::None,
65//! length: Some(Length::Short),
66//! primitive: Primitive::Integer,
67//! style: Style::None,
68//! }),
69//! FormatFragment::Literal("%.".to_string()),
70//! ]
71//! });
72//! ```
73//!
74//! # Runtime Formatting Example
75//!
76//! ```
77//! use pw_format::{Arg, FormatString, FormatStyle};
78//!
79//! let fmt = FormatString::parse_printf("Hello %s, code: 0x%04x!").unwrap();
80//! let output = fmt.format(
81//! &[Arg::Str("world".to_string()), Arg::Uint(42)],
82//! FormatStyle::Printf,
83//! );
84//! assert_eq!(output, "Hello world, code: 0x002a!");
85//! ```
86//!
87//! # Error Formatting Example
88//!
89//! When formatting strings with missing or mismatched arguments, custom error formatters
90//! implementing [`FormatError`] can be supplied:
91//!
92//! ```
93//! use pw_format::{Arg, ConversionSpec, FormatError, FormatString, FormatStyle};
94//!
95//! struct MyErrorFormatter;
96//! impl FormatError for MyErrorFormatter {
97//! type Error = ();
98//! fn format_error(&self, spec: &ConversionSpec, _error: &()) -> String {
99//! format!("<[{} ERROR]>", spec.to_printf())
100//! }
101//! fn format_missing(&self, spec: &ConversionSpec) -> String {
102//! format!("<[{} MISSING]>", spec.to_printf())
103//! }
104//! fn format_type_error(&self, spec: &ConversionSpec, _arg: &Arg) -> String {
105//! format!("<[{} TYPE_ERROR]>", spec.to_printf())
106//! }
107//! }
108//!
109//! let fmt = FormatString::parse_printf("Value: %d").unwrap();
110//! let output = fmt.format_with_errors(&[], FormatStyle::Printf, &MyErrorFormatter);
111//! assert_eq!(output, "Value: <[%d MISSING]>");
112//! ```
113#![deny(missing_docs)]
114
115#[cfg(feature = "proc_macro")]
116pub mod macros;
117
118mod core_fmt;
119mod format_string;
120mod parser_util;
121mod printf;
122
123pub use format_string::{
124 Alignment, Arg, Argument, ConversionSpec, Flag, FormatError, FormatFragment, FormatString,
125 FormatStyle, Length, MinFieldWidth, Precision, Primitive, Style,
126};
127
128#[cfg(test)]
129mod tests;