@@ -19,33 +19,72 @@ use std::{
19
19
20
20
use crate :: internal:: description_renderer:: { List , INDENTATION_SIZE } ;
21
21
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.
26
24
///
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:
28
27
///
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();
33
36
/// ```
34
37
///
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:
39
41
///
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
+ /// ```
45
57
///
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.
49
88
#[ derive( Debug , Default ) ]
50
89
pub struct Description {
51
90
elements : List ,
@@ -101,40 +140,48 @@ impl Description {
101
140
Self { initial_indentation : INDENTATION_SIZE , ..self }
102
141
}
103
142
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.
107
144
///
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.
110
149
///
111
150
/// For instance:
112
151
///
113
152
/// ```
114
153
/// # use googletest::prelude::*;
115
154
/// # 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")))
118
161
/// # .unwrap();
119
162
/// ```
120
163
pub fn bullet_list ( self ) -> Self {
121
164
Self { elements : self . elements . bullet_list ( ) , ..self }
122
165
}
123
166
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.
127
168
///
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.
130
173
///
131
174
/// For instance:
132
175
///
133
176
/// ```
134
177
/// # use googletest::prelude::*;
135
178
/// # 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")))
138
185
/// # .unwrap();
139
186
/// ```
140
187
pub fn enumerate ( self ) -> Self {
@@ -158,12 +205,12 @@ impl Display for Description {
158
205
}
159
206
}
160
207
161
- impl FromIterator < String > for Description {
208
+ impl < ElementT : Into < Cow < ' static , str > > > FromIterator < ElementT > for Description {
162
209
fn from_iter < T > ( iter : T ) -> Self
163
210
where
164
- T : IntoIterator < Item = String > ,
211
+ T : IntoIterator < Item = ElementT > ,
165
212
{
166
- Self { elements : iter. into_iter ( ) . collect ( ) , ..Default :: default ( ) }
213
+ Self { elements : iter. into_iter ( ) . map ( ElementT :: into ) . collect ( ) , ..Default :: default ( ) }
167
214
}
168
215
}
169
216
@@ -176,7 +223,7 @@ impl FromIterator<Description> for Description {
176
223
}
177
224
}
178
225
179
- impl < T : Into < String > > From < T > for Description {
226
+ impl < T : Into < Cow < ' static , str > > > From < T > for Description {
180
227
fn from ( value : T ) -> Self {
181
228
let mut elements = List :: default ( ) ;
182
229
elements. push_literal ( value. into ( ) . into ( ) ) ;
0 commit comments