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
//! This is a libary for creating styled spans of text. The style can be //! something like an ANSI terminal color/format, or it could be some //! markup like enclosing text in html tags. //! //! //! ## Structure //! This crate is subdivided into two modules: [`text`] and [`widget`]. //! //! [`text`] provides string-like functionality for styled pieces of text. //! Methods such as replacing, slicing, and splitting are supported while //! respecting existing style delimiters. //! //! [`widget`] provides functionality for displaying text objects in useful ways, //! such as truncation with a symbol, or repeating a sequence. //! //! ## Usage //! //! ```rust //! use std::borrow::Cow; //! use stylish_stringlike::text::{Joinable, Paintable, Pushable, Replaceable, Sliceable, Span, //! Spans, Tag}; //! use stylish_stringlike::widget::{Fitable, HBox, TextWidget, TruncationStyle}; //! //! let italic = Tag::new("<i>", "</i>"); //! let bold = Tag::new("<b>", "</b>"); //! let underline = Tag::new("<u>", "</u>"); //! //! let foo: Span<Tag> = Span::new(Cow::Borrowed(&italic), Cow::Borrowed("foo")); //! let bar: Span<Tag> = Span::new(Cow::Borrowed(&bold), Cow::Borrowed("bar")); //! //! // Spans of different styles can be joined together. //! let foobar = foo.join(&bar); //! assert_eq!(format!("{}", foobar), "<i>foo</i><b>bar</b>"); //! //! // Perform literal string replacement with the `replace` method. //! let foobaz = foobar.replace("bar", "baz"); //! assert_eq!(format!("{}", foobaz), "<i>foo</i><b>baz</b>"); //! //! let mut buz: Spans<Tag> = Default::default(); //! buz.push(&Span::new(Cow::Borrowed(&underline), Cow::Borrowed("buz"))); //! //! // Replace text with styled text objects instead of string literals. //! let foobuz = foobar.replace("bar", &buz); //! assert_eq!(format!("{}", foobuz), "<i>foo</i><u>buz</u>"); //! //! // Use the `slice` method to slice on bytes. //! let foob = foobar.slice(..4).unwrap(); //! assert_eq!(format!("{}", foob), "<i>foo</i><b>b</b>"); //! //! // Use the `HBox` widget to truncate multiple spans of text to fit in a desired width. //! fn make_spans(style: &Tag, text: &str) -> Spans<Tag> { //! let mut spans: Spans<Tag> = Default::default(); //! let span: Span<Tag> = Span::new(Cow::Borrowed(style), Cow::Borrowed(text)); //! spans = spans.join(&span); //! spans //! } //! let truncation = TruncationStyle::Inner(Some(Span::new( //! Cow::Borrowed(&underline), //! Cow::Borrowed("…"), //! ))); //! let spans = vec![make_spans(&italic, "abcdefg"), make_spans(&bold, "12345678")]; //! let hbox = spans //! .iter() //! .map(|s| { //! let b: Box<dyn Fitable<_>> = //! Box::new(TextWidget::<Spans<_>, TruncationStyle<_>>::new( //! Cow::Borrowed(s), //! Cow::Borrowed(&truncation), //! )); //! b //! }) //! .collect::<HBox<_>>(); //! assert_eq!( //! format!("{}", hbox.truncate(10)), //! "<i>ab</i><u>…</u><i>fg</i><b>12</b><u>…</u><b>78</b>" //! ); //! ``` pub mod text; pub mod widget; #[cfg(test)] mod test { use super::*; use ansi_term::{ANSIString, ANSIStrings, Color, Style}; use std::borrow::Cow; use text::*; use widget::*; fn make_spans(style: &Style, text: &str) -> Spans<Style> { let ansistring: ANSIString = Style::paint(*style, text); let span: Span<Style> = ansistring.into(); let mut spans: Spans<Style> = Default::default(); spans.push(&span); spans } #[test] fn split_path() { let texts = vec![ Color::Black.paint("::"), Color::Red.paint("SomeExtremelyLong"), Color::Blue.paint("::"), Color::Green.paint("RandomAndPoorlyNamed"), Color::Cyan.paint("::"), Color::White.paint("Path"), Color::Yellow.paint("::"), ]; let spans: Spans<_> = texts.iter().map(Span::<Style>::from).collect(); let ellipsis_string = Color::Blue.paint("…"); let ellipsis_span = make_spans(&Color::Blue.normal(), "…"); let truncation = TruncationStyle::Inner(ellipsis_span.clone()); let actual = spans .split("::") .map(|Split { segment, delim }| vec![segment, delim]) .flatten() .flatten() .map(|s| { let foo: Box<dyn Fitable<_>> = Box::new(TextWidget::<Spans<_>, TruncationStyle<_>>::new( Cow::Owned(s), Cow::Borrowed(&truncation), )); foo }) .collect::<HBox<_>>() .truncate(20) .to_string(); let expected = format!( "{}", ANSIStrings(&[ Color::Black.paint("::"), Color::Red.paint("So"), ellipsis_string.clone(), Color::Red.paint("g"), Color::Blue.paint("::"), Color::Green.paint("Ra"), ellipsis_string, Color::Green.paint("d"), Color::Cyan.paint("::"), Color::White.paint("Path"), Color::Yellow.paint("::"), ]) ); assert_eq!(expected, actual); } #[test] fn split_path_2() { let path = Color::Blue.paint("some//path//with//segments"); let span: Span<Style> = path.clone().into(); let spans = { let mut spans: Spans<Style> = Default::default(); spans.push(&span); spans }; let truncation = TruncationStyle::Inner(Some(make_spans(&Color::Blue.normal(), "……"))); let actual = spans .split("::") .map(|Split { segment, delim }| vec![segment, delim]) .flatten() .flatten() .map(|s| { let foo: Box<dyn Fitable<_>> = Box::new(TextWidget::<Spans<Style>, TruncationStyle<_>>::new( Cow::Owned(s), Cow::Borrowed(&truncation), )); foo }) .collect::<HBox<_>>() .truncate(50) .to_string(); let expected = format!("{}", path); assert_eq!(expected, actual); } }