1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! Adapters with which you can easily create more varieties of formatting than the [`std::fmt`]
//! traits ([`fmt::Display`], [`fmt::Debug`], [`fmt::Binary`], [`fmt::Pointer`], etc.) offer,
//! without having to write any more boilerplate than absolutely necessary.
//! You can also easily pass additional data down through the formatting recursion.
//!
//! To create a new format, declare a struct (`struct MyFormat;`) and implement
//! [`Fmt<MyFormat>`](Fmt) for the type you want to be able to format. Then call [`Refmt::refmt()`]
//! to apply the format as a wrapper type around your data.
//!
//! # Example
//!
//! The following code implements a minimal, non-validating TOML emitter.
//! This demonstrates how `manyfmt` can be used to produce complex formatting, that operates
//! within the [`std::fmt`] system without allocating, without writing a new by-reference wrapper
//! type for each case.
//!
//! ```
//! use std::collections::BTreeMap;
//! use std::fmt;
//!
//! use manyfmt::{Fmt, Refmt};
//!
//! struct TomlFile;
//! struct TomlTable;
//!
//! impl<S: AsRef<str>, T: Fmt<TomlTable>> Fmt<TomlFile> for BTreeMap<S, T> {
//!     fn fmt(&self, fmt: &mut fmt::Formatter<'_>, _: &TomlFile) -> fmt::Result {
//!         for (key, table) in self {
//!             writeln!(
//!                 fmt,
//!                 "[{key}]\n{table}",
//!                 key = key.as_ref(),
//!                 table = table.refmt(&TomlTable)
//!             )?;
//!         }
//!         Ok(())
//!     }
//! }
//!
//! impl<S: AsRef<str>, T: fmt::Debug> Fmt<TomlTable> for BTreeMap<S, T> {
//!     fn fmt(&self, fmt: &mut fmt::Formatter<'_>, _: &TomlTable) -> fmt::Result {
//!         for (key, value) in self {
//!             // A real implementation would use TOML-specific value formatting
//!             // rather than `Debug`, which promises nothing.
//!             writeln!(fmt, "{key} = {value:?}", key = key.as_ref())?;
//!         }
//!         Ok(())
//!     }
//! }
//!
//! let data = BTreeMap::from([
//!     ("package", BTreeMap::from([("name", "manyfmt"), ("edition", "2021")])),
//!     ("lib", BTreeMap::from([("name", "manyfmt")])),
//! ]);
//!
//! let text = data.refmt(&TomlFile).to_string();
//!
//! assert_eq!(text,
//! r#"[lib]
//! name = "manyfmt"
//!
//! [package]
//! edition = "2021"
//! name = "manyfmt"
//!
//! "#);
//! ```

#![no_std]
#![forbid(elided_lifetimes_in_paths)]
#![forbid(unsafe_code)]
#![warn(clippy::cast_lossless)]
#![warn(clippy::exhaustive_enums)]
#![warn(clippy::exhaustive_structs)]
#![warn(clippy::missing_panics_doc)]
#![warn(clippy::return_self_not_must_use)]
#![warn(clippy::wrong_self_convention)]
#![warn(missing_docs)]
#![warn(unused_lifetimes)]

#[cfg(any(doc, test))]
#[macro_use]
extern crate std;

use core::fmt;

pub mod formats;

/// Implement this trait to provide a new kind of formatting, `F`, for values of type `Self`.
///
/// The type `F` may be used to carry formatting options, or it may be a simple unit struct
/// which merely serves to select the implementation. See [the crate documentation](crate) for
/// examples.
pub trait Fmt<F: ?Sized> {
    /// Formats `self` as specified by `fopt` into destination `fmt`.
    ///
    /// # Errors
    ///
    /// Returns [`Err`] if and only if `fmt` returns [`Err`]. Implementations should never return an
    /// error in any other circumstance, as this would, for example, cause uses of [`ToString`] or
    /// [`format!`] to panic.
    ///
    /// [`ToString`]: std::string::ToString
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>, fopt: &F) -> fmt::Result;
}

/// Wrap `value` so that when formatted with [`fmt::Debug`] or [`fmt::Display`], it uses
/// the given [`Fmt`] custom format type instead.
///
/// This operation is also available as the extension trait method [`Refmt::refmt()`].
#[inline]
pub fn refmt<'a, F: ?Sized, T: ?Sized>(fopt: &'a F, value: &'a T) -> Wrapper<'a, F, T>
where
    T: Fmt<F>,
{
    Wrapper { fopt, value }
}

/// Extension trait providing the [`.refmt()`](Self::refmt) convenience method.
//---
// Design note: `F` is a parameter of the trait rather than the function so that method lookup will
// propagate through dereferencing.
pub trait Refmt<F: ?Sized>
where
    Self: Fmt<F>,
{
    /// Wrap this value so that when formatted with [`fmt::Debug`] or [`fmt::Display`], it uses
    /// the given [`Fmt`] custom format type instead.
    ///
    /// This operation is also available as the non-trait function [`refmt()`].
    fn refmt<'a>(&'a self, fopt: &'a F) -> Wrapper<'a, F, Self>;
}
impl<F: ?Sized, T: ?Sized + Fmt<F>> Refmt<F> for T {
    #[inline]
    fn refmt<'a>(&'a self, fopt: &'a F) -> Wrapper<'a, F, Self> {
        Wrapper { fopt, value: self }
    }
}

/// Wrapper type to replace the [`fmt::Display`] and [`fmt::Debug`] behavior of its contents with
/// a [`Fmt`] implementation.
///
/// * `F` is the [`Fmt`] formatting type to use.
/// * `T` is the type of value to be printed.
///
/// You can use [`refmt()`] or [`Refmt::refmt()`] to construct this.
///
/// To enable using this wrapper inside [`assert_eq`], it implements [`PartialEq`]
/// (comparing both value and format).
#[derive(Eq, PartialEq)]
pub struct Wrapper<'a, F: ?Sized, T: ?Sized> {
    value: &'a T,
    fopt: &'a F,
}

impl<'a, F: ?Sized, T: ?Sized + Fmt<F>> fmt::Debug for Wrapper<'a, F, T> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        <T as Fmt<F>>::fmt(self.value, fmt, self.fopt)
    }
}
impl<'a, F: ?Sized, T: ?Sized + Fmt<F>> fmt::Display for Wrapper<'a, F, T> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        <T as Fmt<F>>::fmt(self.value, fmt, self.fopt)
    }
}

mod impls {
    use super::*;
    /// Forwards to the referent.
    impl<F, T: Fmt<F>> Fmt<F> for &'_ T {
        fn fmt(&self, fmt: &mut fmt::Formatter<'_>, fopt: &F) -> fmt::Result {
            <T as Fmt<F>>::fmt(&**self, fmt, fopt)
        }
    }
    /// Forwards to the referent.
    impl<F, T: Fmt<F>> Fmt<F> for &'_ mut T {
        fn fmt(&self, fmt: &mut fmt::Formatter<'_>, fopt: &F) -> fmt::Result {
            <T as Fmt<F>>::fmt(&**self, fmt, fopt)
        }
    }
}