stylish_core/formatter.rs
1use crate::{Arguments, Display, Restyle, Result, Style, Write};
2
3#[doc(hidden)] // workaround https://github.com/rust-lang/rust/issues/85522
4#[derive(Clone, Copy, Debug)]
5pub enum Align {
6 Left,
7 Center,
8 Right,
9}
10
11#[doc(hidden)] // workaround https://github.com/rust-lang/rust/issues/85522
12#[derive(Clone, Copy, Debug)]
13pub enum Sign {
14 Plus,
15 Minus,
16}
17
18#[doc(hidden)] // workaround https://github.com/rust-lang/rust/issues/85522
19#[derive(Clone, Copy, Debug)]
20pub enum DebugHex {
21 Lower,
22 Upper,
23}
24
25#[doc(hidden)] // workaround https://github.com/rust-lang/rust/issues/85522
26#[derive(Clone, Copy, Debug, Default)]
27pub struct FormatterArgs<'a> {
28 pub align: Option<Align>,
29 pub sign: Option<Sign>,
30 pub alternate: bool,
31 pub zero: bool,
32 pub width: Option<&'a usize>,
33 pub precision: Option<&'a usize>,
34 pub debug_hex: Option<DebugHex>,
35}
36
37/// A configured output stream.
38///
39/// A `Formatter` wraps a target output stream with a set of configuration
40/// options for formatting of data written to the stream. There is (currently)
41/// no public constructors for `Formatter`, an instance is created and passed to
42/// implementations of [`stylish::Display`] when they are used in
43/// the [`stylish`] macros.
44pub struct Formatter<'a> {
45 style: Style,
46 pub(crate) format: FormatterArgs<'a>,
47 write: &'a mut (dyn Write + 'a),
48}
49
50impl core::fmt::Debug for Formatter<'_> {
51 #[inline]
52 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result {
53 f.debug_struct("Formatter")
54 .field("style", &self.style)
55 .field("format", &self.format)
56 .finish()
57 }
58}
59
60impl<'a> Formatter<'a> {
61 #[inline]
62 pub(crate) fn new(write: &'a mut (dyn Write + 'a)) -> Self {
63 Self {
64 style: Style::default(),
65 format: FormatterArgs::default(),
66 write,
67 }
68 }
69
70 // TODO: All the rest of the std::fmt::Formatter methods
71
72 /// Create a sub-`Formatter` with some styles changed. This may be useful in
73 /// implementations of [`stylish::Display`] to dynamically configure how
74 /// some parts are formatted.
75 ///
76 /// ```rust
77 /// struct Name(&'static str);
78 ///
79 /// impl stylish::Display for Name {
80 /// fn fmt(&self, f: &mut stylish::Formatter<'_>) -> stylish::Result {
81 /// let color = match self.0 {
82 /// "Ferris" => stylish::Color::Red,
83 /// "Gorris" => stylish::Color::Cyan,
84 /// _ => stylish::Color::Default,
85 /// };
86 /// f.with(stylish::Foreground(color)).write_str(self.0)
87 /// }
88 /// }
89 ///
90 /// let formatted = stylish::html::format!("Hello {:s} and {:s}", Name("Ferris"), Name("Gorris"));
91 /// assert_eq!(
92 /// formatted,
93 /// "Hello <span style=color:red>Ferris</span> and <span style=color:cyan>Gorris</span>"
94 /// );
95 /// ```
96 pub fn with(&mut self, restyle: impl Restyle) -> Formatter<'_> {
97 Formatter {
98 write: &mut *self.write,
99 format: self.format,
100 style: self.style.with(restyle),
101 }
102 }
103
104 #[doc(hidden)]
105 /// pub for macros
106 pub fn with_args<'b>(
107 &'b mut self,
108 format: &FormatterArgs<'b>,
109 restyle: impl Restyle,
110 ) -> Formatter<'b> {
111 Formatter {
112 write: &mut *self.write,
113 format: *format,
114 style: self.style.with(restyle),
115 }
116 }
117
118 /// Writes some data to the underlying output stream, using the current
119 /// style.
120 ///
121 /// ```rust
122 /// struct Name(&'static str);
123 ///
124 /// impl stylish::Display for Name {
125 /// fn fmt(&self, f: &mut stylish::Formatter<'_>) -> stylish::Result {
126 /// let color = match self.0 {
127 /// "Ferris" => stylish::Color::Red,
128 /// "Gorris" => stylish::Color::Cyan,
129 /// _ => stylish::Color::Default,
130 /// };
131 /// f.with(stylish::Foreground(color)).write_str(self.0)
132 /// }
133 /// }
134 ///
135 /// let formatted = stylish::html::format!("Hello {:s} and {:s}", Name("Ferris"), Name("Gorris"));
136 /// assert_eq!(
137 /// formatted,
138 /// "Hello <span style=color:red>Ferris</span> and <span style=color:cyan>Gorris</span>"
139 /// );
140 /// ```
141 #[inline]
142 pub fn write_str(&mut self, s: &str) -> Result {
143 self.write.write_str(s, self.style)?;
144 Ok(())
145 }
146
147 /// Writes some formatted data into this instance, overriding the current
148 /// style as appropriate.
149 ///
150 /// ```rust
151 /// struct Name(&'static str);
152 ///
153 /// impl stylish::Display for Name {
154 /// fn fmt(&self, f: &mut stylish::Formatter<'_>) -> stylish::Result {
155 /// match self.0 {
156 /// "Ferris" => f.write_fmt(stylish::format_args!("{:(fg=red)}", self.0)),
157 /// "Gorris" => f.write_fmt(stylish::format_args!("{:(fg=cyan)}", self.0)),
158 /// _ => f.write_fmt(stylish::format_args!("{}", self.0)),
159 /// }
160 /// }
161 /// }
162 ///
163 /// let formatted = stylish::html::format!("Hello {:s} and {:s}", Name("Ferris"), Name("Gorris"));
164 /// assert_eq!(
165 /// formatted,
166 /// "Hello <span style=color:red>Ferris</span> and <span style=color:cyan>Gorris</span>"
167 /// );
168 /// ```
169 #[inline]
170 pub fn write_fmt(&mut self, args: Arguments<'_>) -> Result {
171 args.fmt(self)?;
172 Ok(())
173 }
174}
175
176impl<'a> Write for Formatter<'a> {
177 #[inline]
178 fn write_str(&mut self, s: &str, style: Style) -> Result {
179 self.with(style).write_str(s)
180 }
181
182 #[inline]
183 fn write_fmt(&mut self, args: Arguments<'_>) -> Result {
184 self.write_fmt(args)
185 }
186}
187
188impl<'a> core::fmt::Write for Formatter<'a> {
189 #[inline]
190 fn write_str(&mut self, s: &str) -> Result {
191 self.write_str(s)
192 }
193}