@@ -19,33 +19,72 @@ use std::{
1919
2020use crate :: internal:: description_renderer:: { List , INDENTATION_SIZE } ;
2121
22- /// Helper structure to build better output of
23- /// [`Matcher::describe`][crate::matcher::Matcher::describe] and
24- /// [`Matcher::explain_match`][crate::matcher::Matcher::explain_match]. This
25- /// is especially useful with composed matchers and matchers over containers.
22+ /// A structured description, either of a (composed) matcher or of an
23+ /// assertion failure.
2624///
27- /// It provides simple operations to lazily format lists of strings.
25+ /// One can compose blocks of text into a `Description`. Each one appears on a
26+ /// new line. For example:
2827///
29- /// Usage:
30- /// ```ignore
31- /// let iter: impl Iterator<String> = ...
32- /// format!("{}", iter.collect::<Description>().indent().bullet_list())
28+ /// ```
29+ /// # use googletest::prelude::*;
30+ /// # use googletest::description::Description;
31+ /// let description = Description::new()
32+ /// .text("A block")
33+ /// .text("Another block");
34+ /// verify_that!(description, displays_as(eq("A block\nAnother block")))
35+ /// # .unwrap();
3336/// ```
3437///
35- /// To construct a [`Description`], use `Iterator<Item=String>::collect()`.
36- /// Each element of the collected iterator will be separated by a
37- /// newline when displayed. The elements may be multi-line, but they will
38- /// nevertheless be indented consistently.
38+ /// One can embed nested descriptions into a `Description`. The resulting
39+ /// nested description is then rendered with an additional level of
40+ /// indentation. For example:
3941///
40- /// Note that a newline will only be added between each element, but not
41- /// after the last element. This makes it simpler to keep
42- /// [`Matcher::describe`][crate::matcher::Matcher::describe]
43- /// and [`Matcher::explain_match`][crate::matcher::Matcher::explain_match]
44- /// consistent with simpler [`Matchers`][crate::matcher::Matcher].
42+ /// ```
43+ /// # use googletest::prelude::*;
44+ /// # use googletest::description::Description;
45+ /// let inner_description = Description::new()
46+ /// .text("A block")
47+ /// .text("Another block");
48+ /// let outer_description = Description::new()
49+ /// .text("Header")
50+ /// .nested(inner_description);
51+ /// verify_that!(outer_description, displays_as(eq("\
52+ /// Header
53+ /// A block
54+ /// Another block")))
55+ /// # .unwrap();
56+ /// ```
4557///
46- /// They can also be indented, enumerated and or
47- /// bullet listed if [`Description::indent`], [`Description::enumerate`], or
48- /// respectively [`Description::bullet_list`] has been called.
58+ /// One can also enumerate or bullet list the elements of a `Description`:
59+ ///
60+ /// ```
61+ /// # use googletest::prelude::*;
62+ /// # use googletest::description::Description;
63+ /// let description = Description::new()
64+ /// .text("First item")
65+ /// .text("Second item")
66+ /// .bullet_list();
67+ /// verify_that!(description, displays_as(eq("\
68+ /// * First item
69+ /// * Second item")))
70+ /// # .unwrap();
71+ /// ```
72+ ///
73+ /// One can construct a `Description` from a [`String`] or a string slice, an
74+ /// iterator thereof, or from an iterator over other `Description`s:
75+ ///
76+ /// ```
77+ /// # use googletest::description::Description;
78+ /// let single_element_description: Description =
79+ /// "A single block description".into();
80+ /// let two_element_description: Description =
81+ /// ["First item", "Second item"].into_iter().collect();
82+ /// let two_element_description_from_strings: Description =
83+ /// ["First item".to_string(), "Second item".to_string()].into_iter().collect();
84+ /// ```
85+ ///
86+ /// No newline is added after the last element during rendering. This makes it
87+ /// easier to support single-line matcher descriptions and match explanations.
4988#[ derive( Debug , Default ) ]
5089pub struct Description {
5190 elements : List ,
@@ -101,40 +140,48 @@ impl Description {
101140 Self { initial_indentation : INDENTATION_SIZE , ..self }
102141 }
103142
104- /// Bullet lists the elements of [`self`].
105- ///
106- /// This operation will be performed lazily when [`self`] is displayed.
143+ /// Instructs this instance to render its elements as a bullet list.
107144 ///
108- /// Note that this will only bullet list each element, not each line
109- /// in each element.
145+ /// Each element (from either [`Description::text`] or
146+ /// [`Description::nested`]) is rendered as a bullet point. If an element
147+ /// contains multiple lines, the following lines are aligned with the first
148+ /// one in the block.
110149 ///
111150 /// For instance:
112151 ///
113152 /// ```
114153 /// # use googletest::prelude::*;
115154 /// # use googletest::description::Description;
116- /// let description = std::iter::once("A B C\nD E F".to_string()).collect::<Description>();
117- /// verify_that!(description.bullet_list(), displays_as(eq("* A B C\n D E F")))
155+ /// let description = Description::new()
156+ /// .text("First line\nsecond line")
157+ /// .bullet_list();
158+ /// verify_that!(description, displays_as(eq("\
159+ /// * First line
160+ /// second line")))
118161 /// # .unwrap();
119162 /// ```
120163 pub fn bullet_list ( self ) -> Self {
121164 Self { elements : self . elements . bullet_list ( ) , ..self }
122165 }
123166
124- /// Enumerates the elements of [`self`].
125- ///
126- /// This operation will be performed lazily when [`self`] is displayed.
167+ /// Instructs this instance to render its elements as an enumerated list.
127168 ///
128- /// Note that this will only enumerate each element, not each line in
129- /// each element.
169+ /// Each element (from either [`Description::text`] or
170+ /// [`Description::nested`]) is rendered with its zero-based index. If an
171+ /// element contains multiple lines, the following lines are aligned with
172+ /// the first one in the block.
130173 ///
131174 /// For instance:
132175 ///
133176 /// ```
134177 /// # use googletest::prelude::*;
135178 /// # use googletest::description::Description;
136- /// let description = std::iter::once("A B C\nD E F".to_string()).collect::<Description>();
137- /// verify_that!(description.enumerate(), displays_as(eq("0. A B C\n D E F")))
179+ /// let description = Description::new()
180+ /// .text("First line\nsecond line")
181+ /// .enumerate();
182+ /// verify_that!(description, displays_as(eq("\
183+ /// 0. First line
184+ /// second line")))
138185 /// # .unwrap();
139186 /// ```
140187 pub fn enumerate ( self ) -> Self {
@@ -158,12 +205,12 @@ impl Display for Description {
158205 }
159206}
160207
161- impl FromIterator < String > for Description {
208+ impl < ElementT : Into < Cow < ' static , str > > > FromIterator < ElementT > for Description {
162209 fn from_iter < T > ( iter : T ) -> Self
163210 where
164- T : IntoIterator < Item = String > ,
211+ T : IntoIterator < Item = ElementT > ,
165212 {
166- Self { elements : iter. into_iter ( ) . collect ( ) , ..Default :: default ( ) }
213+ Self { elements : iter. into_iter ( ) . map ( ElementT :: into ) . collect ( ) , ..Default :: default ( ) }
167214 }
168215}
169216
@@ -176,7 +223,7 @@ impl FromIterator<Description> for Description {
176223 }
177224}
178225
179- impl < T : Into < String > > From < T > for Description {
226+ impl < T : Into < Cow < ' static , str > > > From < T > for Description {
180227 fn from ( value : T ) -> Self {
181228 let mut elements = List :: default ( ) ;
182229 elements. push_literal ( value. into ( ) . into ( ) ) ;
0 commit comments