Skip to content

Commit 085864b

Browse files
committed
initial sketch of styling API
1 parent 61b1f8d commit 085864b

File tree

2 files changed

+254
-0
lines changed

2 files changed

+254
-0
lines changed

src/.write.rs.pending-snap

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,57 @@
105105
{"run_id":"1765352164-402454609","line":1522,"new":null,"old":null}
106106
{"run_id":"1765352164-402454609","line":1237,"new":null,"old":null}
107107
{"run_id":"1765352164-402454609","line":1723,"new":null,"old":null}
108+
{"run_id":"1765630839-195004518","line":1219,"new":null,"old":null}
109+
{"run_id":"1765630839-195004518","line":1292,"new":null,"old":null}
110+
{"run_id":"1765630839-195004518","line":1193,"new":null,"old":null}
111+
{"run_id":"1765630839-195004518","line":1316,"new":null,"old":null}
112+
{"run_id":"1765630839-195004518","line":1342,"new":null,"old":null}
113+
{"run_id":"1765630839-195004518","line":1369,"new":null,"old":null}
114+
{"run_id":"1765630839-195004518","line":1559,"new":null,"old":null}
115+
{"run_id":"1765630839-195004518","line":1269,"new":null,"old":null}
116+
{"run_id":"1765630839-195004518","line":1167,"new":null,"old":null}
117+
{"run_id":"1765630839-195004518","line":1588,"new":null,"old":null}
118+
{"run_id":"1765630839-195004518","line":1700,"new":null,"old":null}
119+
{"run_id":"1765630839-195004518","line":1531,"new":null,"old":null}
120+
{"run_id":"1765630839-195004518","line":1643,"new":null,"old":null}
121+
{"run_id":"1765630839-195004518","line":1437,"new":null,"old":null}
122+
{"run_id":"1765630839-195004518","line":1776,"new":null,"old":null}
123+
{"run_id":"1765630839-195004518","line":1671,"new":null,"old":null}
124+
{"run_id":"1765630839-195004518","line":1053,"new":null,"old":null}
125+
{"run_id":"1765630839-195004518","line":1413,"new":null,"old":null}
126+
{"run_id":"1765630839-195004518","line":1497,"new":null,"old":null}
127+
{"run_id":"1765630839-195004518","line":1617,"new":null,"old":null}
128+
{"run_id":"1765630839-195004518","line":1115,"new":null,"old":null}
129+
{"run_id":"1765630839-195004518","line":1464,"new":null,"old":null}
130+
{"run_id":"1765630839-195004518","line":1140,"new":null,"old":null}
131+
{"run_id":"1765630839-195004518","line":1068,"new":null,"old":null}
132+
{"run_id":"1765630839-195004518","line":1090,"new":null,"old":null}
133+
{"run_id":"1765630839-195004518","line":1732,"new":null,"old":null}
134+
{"run_id":"1765630839-195004518","line":1246,"new":null,"old":null}
135+
{"run_id":"1765634285-448173956","line":1219,"new":null,"old":null}
136+
{"run_id":"1765634285-448173956","line":1193,"new":null,"old":null}
137+
{"run_id":"1765634285-448173956","line":1292,"new":null,"old":null}
138+
{"run_id":"1765634285-448173956","line":1369,"new":null,"old":null}
139+
{"run_id":"1765634285-448173956","line":1316,"new":null,"old":null}
140+
{"run_id":"1765634285-448173956","line":1342,"new":null,"old":null}
141+
{"run_id":"1765634285-448173956","line":1559,"new":null,"old":null}
142+
{"run_id":"1765634285-448173956","line":1269,"new":null,"old":null}
143+
{"run_id":"1765634285-448173956","line":1167,"new":null,"old":null}
144+
{"run_id":"1765634285-448173956","line":1588,"new":null,"old":null}
145+
{"run_id":"1765634285-448173956","line":1776,"new":null,"old":null}
146+
{"run_id":"1765634285-448173956","line":1700,"new":null,"old":null}
147+
{"run_id":"1765634285-448173956","line":1053,"new":null,"old":null}
148+
{"run_id":"1765634285-448173956","line":1413,"new":null,"old":null}
149+
{"run_id":"1765634285-448173956","line":1140,"new":null,"old":null}
150+
{"run_id":"1765634285-448173956","line":1437,"new":null,"old":null}
151+
{"run_id":"1765634285-448173956","line":1671,"new":null,"old":null}
152+
{"run_id":"1765634285-448173956","line":1643,"new":null,"old":null}
153+
{"run_id":"1765634285-448173956","line":1464,"new":null,"old":null}
154+
{"run_id":"1765634285-448173956","line":1531,"new":null,"old":null}
155+
{"run_id":"1765634285-448173956","line":1617,"new":null,"old":null}
156+
{"run_id":"1765634285-448173956","line":1068,"new":null,"old":null}
157+
{"run_id":"1765634285-448173956","line":1497,"new":null,"old":null}
158+
{"run_id":"1765634285-448173956","line":1732,"new":null,"old":null}
159+
{"run_id":"1765634285-448173956","line":1115,"new":null,"old":null}
160+
{"run_id":"1765634285-448173956","line":1090,"new":null,"old":null}
161+
{"run_id":"1765634285-448173956","line":1246,"new":null,"old":null}

src/lib.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub use crate::{
1212
draw::{ColorGenerator, Fmt},
1313
source::{sources, Cache, FileCache, FnCache, Line, Source},
1414
};
15+
use std::sync::Arc;
1516
pub use yansi::Color;
1617

1718
#[cfg(any(feature = "concolor", doc))]
@@ -120,6 +121,205 @@ impl<Id: fmt::Debug + Hash + PartialEq + Eq + ToOwned> Span for (Id, RangeInclus
120121
}
121122
}
122123

124+
/// A trait for messages like raw strings or styled strings
125+
/// note that display should be implemnted without style
126+
pub trait Message {
127+
/// what to render in the report
128+
fn render(&self, f: &mut fmt::Formatter, with_style: bool, color: Option<Color>)
129+
-> fmt::Result;
130+
}
131+
132+
/// an object that can be styled
133+
pub trait Styleble<Style, Output> {
134+
/// apply style to this type
135+
fn with_style(self, s: Style) -> Output;
136+
}
137+
138+
/// simple message that can be used for rendring
139+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
140+
pub struct BasicMessage(String);
141+
142+
impl<T: ToString> From<T> for BasicMessage {
143+
fn from(s: T) -> Self {
144+
Self(s.to_string())
145+
}
146+
}
147+
148+
impl Message for BasicMessage {
149+
fn render(
150+
&self,
151+
f: &mut fmt::Formatter,
152+
_with_style: bool,
153+
color: Option<Color>,
154+
) -> fmt::Result {
155+
match color {
156+
Some(c) => write!(f, "{}", self.0.as_str().fg(c)),
157+
None => write!(f, "{}", self.0),
158+
}
159+
}
160+
}
161+
162+
/// simple colored message that can be used for rendring
163+
/// the color takes precednce over defualt color
164+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
165+
pub struct ColoredMessage(String, Option<Color>);
166+
167+
impl Message for ColoredMessage {
168+
fn render(
169+
&self,
170+
f: &mut fmt::Formatter,
171+
with_style: bool,
172+
_color: Option<Color>,
173+
) -> fmt::Result {
174+
match self.1.filter(|_| with_style) {
175+
Some(c) => write!(f, "{}", self.0.as_str().fg(c)),
176+
None => write!(f, "{}", self.0),
177+
}
178+
}
179+
}
180+
181+
impl Styleble<Color, ColoredMessage> for String {
182+
fn with_style(self, c: Color) -> ColoredMessage {
183+
ColoredMessage(self, Some(c))
184+
}
185+
}
186+
187+
/// a wrapper type for functions that can be used with `FuncMessage`
188+
pub type ArcMessage = Arc<dyn Fn(&mut fmt::Formatter, bool, Option<Color>) -> fmt::Result>;
189+
190+
/// A message captured through a closure
191+
/// this is useful for when you want a more elaborate message
192+
/// the output of format_text is currently this type
193+
#[derive(Clone)]
194+
pub struct FuncMessage(ArcMessage);
195+
impl Message for FuncMessage {
196+
fn render(
197+
&self,
198+
f: &mut fmt::Formatter,
199+
with_style: bool,
200+
color: Option<Color>,
201+
) -> fmt::Result {
202+
self.0(f, with_style, color)
203+
}
204+
}
205+
206+
impl FuncMessage {
207+
fn new(x: ArcMessage) -> Self {
208+
Self(x.into())
209+
}
210+
}
211+
212+
/// A message taken by refrence useful for when you need Copy
213+
#[derive(Debug,Hash, PartialEq, Eq)]
214+
pub struct RefMessage<'a, M: Message>(pub &'a M);
215+
impl<M: Message> Message for RefMessage<'_, M> {
216+
fn render(
217+
&self,
218+
f: &mut fmt::Formatter,
219+
with_style: bool,
220+
color: Option<Color>,
221+
) -> fmt::Result {
222+
self.0.render(f, with_style, color)
223+
}
224+
}
225+
226+
impl<M: Message> Clone for RefMessage<'_, M> {
227+
fn clone(&self) -> Self { Self(self.0) }
228+
}
229+
impl<M: Message> Copy for RefMessage<'_, M> {}
230+
231+
#[doc(hidden)]
232+
#[allow(dead_code)]
233+
struct DisplayMessage<M: Message> {
234+
m: M,
235+
with_style: bool,
236+
color: Option<Color>,
237+
}
238+
239+
impl<M: Message> Display for DisplayMessage<M> {
240+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241+
self.m.render(f, self.with_style, self.color)
242+
}
243+
}
244+
245+
/// format text into `FuncMessage` that can then be displayed
246+
/// this is just syntax sugar around making a function
247+
#[macro_export]
248+
macro_rules! format_text {
249+
($fmt:literal $(, $arg:expr)* $(,)?) => {{
250+
$crate::FuncMessage::new(std::sync::Arc::new(move |f: &mut std::fmt::Formatter<'_>,
251+
with_style: bool,
252+
color: Option<$crate::Color>| {
253+
write!(
254+
f,
255+
$fmt,
256+
$( DisplayMessage {
257+
m: RefMessage(&$arg),
258+
with_style,
259+
color
260+
} ),*
261+
)
262+
}))
263+
}};
264+
}
265+
266+
#[test]
267+
fn format_text_no_style() {
268+
use yansi::Color;
269+
270+
let msg = "hello".to_string().with_style(Color::Red);
271+
let m = format_text!("test: {}", msg);
272+
273+
let out = format!(
274+
"{}",
275+
DisplayMessage {
276+
m,
277+
with_style: false,
278+
color: None,
279+
}
280+
);
281+
282+
assert_eq!(out, "test: hello");
283+
}
284+
285+
#[test]
286+
fn format_text_emits_red_ansi() {
287+
let msg = "hello".to_string().with_style(Color::Red);
288+
let m = format_text!("X {} X", msg);
289+
290+
let out = format!(
291+
"{}",
292+
DisplayMessage {
293+
m,
294+
with_style: true,
295+
color: None,
296+
}
297+
);
298+
299+
let expected = format!("X {} X", "\x1b[31mhello\x1b[0m");
300+
301+
assert_eq!(out, expected);
302+
}
303+
304+
// /// complex message capable of printing anything
305+
// #[derive(Clone,Debug)]
306+
// pub enum RitchMessage{
307+
// /// just a simple string
308+
// Basic(BasicMessage),
309+
310+
// /// custom functions that can render anything
311+
// Custom(Arc<dyn Message>)
312+
// }
313+
314+
// impl Message for RitchMessage {
315+
// fn render(&self,f: &mut fmt::Formatter,with_style:bool,color:Option<Color>) -> fmt::Result{
316+
// match self {
317+
// RitchMessage::Basic(x)=>x.render(f,with_style,color),
318+
// RitchMessage::Custom(x)=>x.render(f,with_style,color),
319+
// }
320+
// }
321+
// }
322+
123323
/// A type that represents the way a label should be displayed.
124324
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
125325
struct LabelDisplay {

0 commit comments

Comments
 (0)