Skip to content

Commit 577d7e4

Browse files
committed
feat: macros
1 parent e63c114 commit 577d7e4

File tree

10 files changed

+640
-22
lines changed

10 files changed

+640
-22
lines changed

.github/workflows/test.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ jobs:
5959
run:
6060
cargo test ${{ matrix.features }} --all-targets --no-fail-fast --workspace
6161
- name: Doc Test
62+
if: "matrix.features == '--all-features'"
6263
run:
6364
cargo test ${{ matrix.features }} --doc --no-fail-fast --workspace
6465
- name: Build Docs

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
/target
2-
/Cargo.lock
1+
target
2+
Cargo.lock

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
- **Breaking Changed**: `format` and `write` take `Context` trait, this breaks type inference
1010
- `i` allows access to outer variables
1111

12+
### Added
13+
- `set_{trait}` functions on `Formattable`
14+
- Macros for format, write and print
15+
1216
## [0.4.0] - 2023-03-01
1317
### Changed
1418
- **Breaking Change**: Made error enums `non_exhaustive`

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pointer = []
1919
iter = []
2020

2121
[dev-dependencies]
22+
assert-dbg = { path = "assert-dbg" }
2223
collection_literals = "1.0.1"
2324
derive_more = "0.99.17"
2425
proptest = "1.1.0"

assert-dbg/Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "assert-dbg"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]

assert-dbg/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#[macro_export]
2+
macro_rules! assert_dbg {
3+
($lhs:expr, $rhs:expr $(, $($fmt:tt)*)?) => {
4+
::std::assert_eq!(::std::format!("{:?}", $lhs), $rhs$(, $($fmt)*)?);
5+
};
6+
}

src/fmt/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ pub(crate) fn format_value(
9797
zero: bool,
9898
trait_: TraitSpec,
9999
idx: usize,
100-
#[allow(unused)]
101-
context: &impl Context,
100+
#[allow(unused)] context: &impl Context,
102101
) -> Result<()> {
103102
call_format_value! {
104103
match out, value, width, precision, alignment, sign, hash, zero, trait_, idx {

src/formattable.rs

+69-16
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,46 @@ pub struct Formattable<'a> {
2525
iter: Option<&'a [Formattable<'a>]>,
2626
}
2727

28+
struct PlainString(&'static str);
29+
30+
impl Debug for PlainString {
31+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32+
write!(f, "{}", self.0)
33+
}
34+
}
35+
36+
macro_rules! formattable_debug {
37+
($($field:ident $(: $feature:literal)?),+ $(,)?) => {
38+
impl Debug for Formattable<'_> {
39+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
let mut tuple = f.debug_tuple("Formattable");
41+
$(
42+
$(#[cfg(feature = $feature)])?
43+
if self.$field.is_some() {
44+
tuple.field(&PlainString(stringify!($field)));
45+
}
46+
)*
47+
tuple.finish()
48+
}
49+
}
50+
};
51+
}
52+
53+
formattable_debug! {
54+
debug: "debug",
55+
display,
56+
binary: "number",
57+
lower_exp: "number",
58+
lower_hex: "number",
59+
octal: "number",
60+
upper_exp: "number",
61+
upper_hex: "number",
62+
pointer: "pointer",
63+
iter: "iter"
64+
}
65+
2866
macro_rules! formattable_fn {
29-
(($($cfg:tt)*), ($doc:expr), $name:ident, $builder:ident<$($traits:ident),*> $($fields:ident),+) => {
67+
(($($cfg:tt)*), ($doc:expr), $name:ident, $builder:ident, $setter:ident <$($traits:ident),*> $($fields:ident),+) => {
3068
/// Creates a [`Formattable`] from a value implementing
3169
#[doc = $doc]
3270
$($cfg)* pub fn $name<T: $($traits+)*>(value: &'a T) -> Self {
@@ -42,36 +80,41 @@ macro_rules! formattable_fn {
4280
$(self.$fields = Some(value);)*
4381
self
4482
}
83+
/// Sets implementation for
84+
#[doc = $doc]
85+
$($cfg)* pub fn $setter<T: $($traits+)*>(&mut self, value: &'a T) {
86+
$(self.$fields = Some(value);)*
87+
}
4588
};
46-
(($($cfg:tt)*), (), $name:ident, $builder:ident, $getter:ident<$trait:ident>) => {
47-
formattable_fn!(($($cfg)*), (concat!("[`",stringify!($trait),"`]")), $name, $builder<$trait> $name);
89+
(($($cfg:tt)*), (), $name:ident, $builder:ident, $setter:ident, $getter:ident<$trait:ident>) => {
90+
formattable_fn!(($($cfg)*), (concat!("[`",stringify!($trait),"`]")), $name, $builder, $setter<$trait> $name);
4891
$($cfg)* pub(crate) fn $getter(&self) -> Result<&dyn $trait, Trait> {
4992
self.$name.ok_or(Trait::$trait)
5093
}
5194
};
5295
}
5396
macro_rules! formattable {
54-
[$($($cfg:literal, $($doc:literal,)?)? $name:ident, $builder:ident$(, $getter:ident)?<$($traits:ident),*> $($fields:ident),*;)*] => {
97+
[$($($cfg:literal, $($doc:literal,)?)? $name:ident, $builder:ident, $setter:ident $(, $getter:ident)?<$($traits:ident),*> $($fields:ident),*;)*] => {
5598
impl<'a> Formattable<'a> {
56-
$(formattable_fn!(($(#[cfg(feature=$cfg)])?), ($($($doc)?)?), $name, $builder$(, $getter)?<$($traits),*> $($fields),*);)*
99+
$(formattable_fn!(($(#[cfg(feature=$cfg)])?), ($($($doc)?)?), $name, $builder, $setter $(, $getter)?<$($traits),*> $($fields),*);)*
57100
}
58101
};
59102
}
60103

61104
formattable![
62-
"debug", "[`Debug`] and [`Display`]", debug_display, and_debug_display<Debug, Display> debug, display;
63-
"debug", debug, and_debug, get_debug<Debug>;
64-
display, and_display, get_display<Display>;
65-
"number", "[`Debug`], [`Display`], [`Octal`], [`LowerHex`], [`UpperHex`], [`Binary`], [`LowerExp`] and [`UpperExp`]", integer, and_integer<Binary, Debug, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex>
105+
"debug", "[`Debug`] and [`Display`]", debug_display, and_debug_display, set_debug_display<Debug, Display> debug, display;
106+
"debug", debug, and_debug, set_debug, get_debug<Debug>;
107+
display, and_display, set_display, get_display<Display>;
108+
"number", "[`Debug`], [`Display`], [`Octal`], [`LowerHex`], [`UpperHex`], [`Binary`], [`LowerExp`] and [`UpperExp`]", integer, and_integer, set_integer<Binary, Debug, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex>
66109
binary, debug, display, lower_exp, lower_hex, octal, upper_exp, upper_hex;
67-
"number", "[`Debug`], [`Display`], [`LowerExp`] and [`UpperExp`]", float, and_float<Debug, Display, LowerExp, UpperExp>
110+
"number", "[`Debug`], [`Display`], [`LowerExp`] and [`UpperExp`]", float, and_float, set_float<Debug, Display, LowerExp, UpperExp>
68111
debug, display, lower_exp, upper_exp;
69-
"number", binary, and_binary, get_binary<Binary>;
70-
"number", lower_exp, and_lower_exp, get_lower_exp<LowerExp>;
71-
"number", lower_hex, and_lower_hex, get_lower_hex<LowerHex>;
72-
"number", octal, and_octal, get_octal<Octal>;
73-
"number", upper_exp, and_upper_exp, get_upper_exp<UpperExp>;
74-
"number", upper_hex, and_upper_hex, get_upper_hex<UpperHex>;
112+
"number", binary, and_binary, set_binary, get_binary<Binary>;
113+
"number", lower_exp, and_lower_exp, set_lower_exp, get_lower_exp<LowerExp>;
114+
"number", lower_hex, and_lower_hex, set_lower_hex, get_lower_hex<LowerHex>;
115+
"number", octal, and_octal, set_octal, get_octal<Octal>;
116+
"number", upper_exp, and_upper_exp, set_upper_exp, get_upper_exp<UpperExp>;
117+
"number", upper_hex, and_upper_hex, set_upper_hex, get_upper_hex<UpperHex>;
75118
];
76119

77120
#[cfg(feature = "pointer")]
@@ -87,6 +130,11 @@ impl<'a> Formattable<'a> {
87130
self
88131
}
89132

133+
/// Sets implementation for [`Pointer`]
134+
pub fn set_pointer<T: Pointer>(&mut self, value: &'a T) {
135+
self.pointer = Some(PointerWrapper(value));
136+
}
137+
90138
pub(crate) fn get_pointer(&self) -> Result<PointerWrapper, Trait> {
91139
self.pointer.ok_or(Trait::Pointer)
92140
}
@@ -105,6 +153,11 @@ impl<'a> Formattable<'a> {
105153
self
106154
}
107155

156+
/// Sets implementation for mapping operations
157+
pub fn set_iter(&mut self, value: &'a [Formattable<'a>]) {
158+
self.iter = Some(value);
159+
}
160+
108161
pub(crate) fn get_iter(&self) -> Result<&'a [Formattable<'a>], Trait> {
109162
self.iter.ok_or(Trait::Iter)
110163
}

src/lib.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Runtime implementation of [`format!`].
1+
//! Runtime implementation of [`format!`](std::format).
22
//!
33
//! # `std::fmt` compatible formatting
44
//!
@@ -67,7 +67,26 @@ assert_eq!(
6767
```"#
6868
)]
6969

70-
//! See [`format`](format()) and [`write`](write()) for details.
70+
//! See [`format()`] and [`write()`] for details.
71+
//!
72+
//! # Macros
73+
//!
74+
//! To simplify creating contexts, some macros are provided.
75+
//!
76+
//! - [`context!`] creates a [`HashMap<&str, Formattable>`](HashMap) to be used
77+
//! with [`format()`].
78+
#![cfg_attr(
79+
feature = "iter",
80+
doc = r"- [`list!`] creates a [`Formattable`] implementing supporting [iter](`i`-iter-format)."
81+
)]
82+
//! - [`iformat!`] and [`iwrite!`] macros matching the behaviour of [`format()`]
83+
//! and [`write()`] but allowing to specify context directly.
84+
//! - Most of std's formatting macros are supported with an `i` prefix:
85+
//! - [`iwriteln!`]
86+
//! - [`iprint!`]
87+
//! - [`iprintln!`]
88+
//! - [`ieprint!`]
89+
//! - [`ieprintln!`]
7190
//!
7291
//! # Features
7392
//! By default only [`Display`] is supported, the rest of the
@@ -106,6 +125,7 @@ mod formattable;
106125
pub use formattable::*;
107126
mod parser;
108127
use parser::*;
128+
mod macros;
109129

110130
type Result<T = (), E = Error> = std::result::Result<T, E>;
111131

0 commit comments

Comments
 (0)