fluent_assertions/assertions/
collection_assertion.rs1use super::Assertion;
2use std::fmt::Debug;
3
4pub trait CollectionAssertion<T> {
15 fn be_empty(self) -> Self;
24 fn not_be_empty(self) -> Self;
33 fn have_length(self, length: usize) -> Self;
42 fn contain(self, expected: &T) -> Self
51 where
52 T: PartialEq;
53}
54
55#[track_caller]
56fn assert_empty<T: Debug>(items: &[T]) {
57 assert!(
58 items.is_empty(),
59 "Expected collection to be empty, but got {:?}",
60 items
61 );
62}
63
64#[track_caller]
65fn assert_not_empty<T>(items: &[T]) {
66 assert!(
67 !items.is_empty(),
68 "Expected collection to not be empty, but got empty collection"
69 );
70}
71
72#[track_caller]
73fn assert_length<T>(items: &[T], length: usize) {
74 assert!(
75 items.len() == length,
76 "Expected collection to have length {}, but it had length {}",
77 length,
78 items.len()
79 );
80}
81
82#[track_caller]
83fn assert_contains<T: PartialEq + Debug>(items: &[T], expected: &T) {
84 assert!(
85 items.contains(expected),
86 "Expected collection to contain {:?}, but it didn't",
87 expected
88 );
89}
90
91macro_rules! impl_collection_assertion {
96 ([$($generics:tt)*] $ty:ty, |$this:ident| $slice:expr) => {
97 impl<$($generics)*> CollectionAssertion<T> for Assertion<$ty> {
98 #[track_caller]
99 fn be_empty(self) -> Self {
100 let $this = &self;
101 assert_empty($slice);
102 self
103 }
104
105 #[track_caller]
106 fn not_be_empty(self) -> Self {
107 let $this = &self;
108 assert_not_empty($slice);
109 self
110 }
111
112 #[track_caller]
113 fn have_length(self, length: usize) -> Self {
114 let $this = &self;
115 assert_length($slice, length);
116 self
117 }
118
119 #[track_caller]
120 fn contain(self, expected: &T) -> Self
121 where
122 T: PartialEq,
123 {
124 let $this = &self;
125 assert_contains($slice, expected);
126 self
127 }
128 }
129 };
130}
131
132impl_collection_assertion!([T: Debug] Vec<T>, |a| &a.value);
133impl_collection_assertion!([T: Debug] &[T], |a| a.value);
134impl_collection_assertion!([T: Debug, const N: usize] [T; N], |a| &a.value);
135impl_collection_assertion!([T: Debug] &Vec<T>, |a| a.value);
136
137#[cfg(test)]
138mod tests {
139 use crate::assertions::*;
140 use rstest::*;
141
142 #[test]
143 fn test_vec_assertions() {
144 let numbers = vec![1, 2, 3];
145 numbers.should().not_be_empty().have_length(3).contain(&2);
146 }
147
148 #[test]
149 fn test_slice_assertions() {
150 let numbers = [1, 2, 3];
151 numbers
152 .as_slice()
153 .should()
154 .not_be_empty()
155 .have_length(3)
156 .contain(&2);
157 }
158
159 #[test]
160 fn test_array_assertions() {
161 [1, 2, 3].should().not_be_empty().have_length(3).contain(&2);
162 }
163
164 #[test]
165 fn test_vec_ref_assertions() {
166 let numbers = vec![1, 2, 3];
167 (&numbers)
168 .should()
169 .not_be_empty()
170 .have_length(3)
171 .contain(&2);
172 }
173
174 #[rstest]
175 #[case(Vec::<i32>::new())]
176 #[case(vec![])]
177 fn should_be_empty(#[case] input: Vec<i32>) {
178 input.should().be_empty();
179 }
180
181 #[test]
182 fn empty_slice_should_be_empty() {
183 let empty: [i32; 0] = [];
184 empty.as_slice().should().be_empty();
185 }
186
187 #[test]
188 fn empty_array_should_be_empty() {
189 let empty: [i32; 0] = [];
190 empty.should().be_empty();
191 }
192
193 #[rstest]
194 #[case(vec!["hello".to_string()])]
195 fn should_contain_string(#[case] input: Vec<String>) {
196 input.should().contain(&String::from("hello"));
197 }
198
199 #[test]
200 #[should_panic(expected = "Expected collection to contain 4")]
201 fn contain_panics_when_element_missing() {
202 vec![1, 2, 3].should().contain(&4);
203 }
204
205 #[test]
206 #[should_panic(expected = "Expected collection to be empty")]
207 fn be_empty_panics_when_not_empty() {
208 vec![1].should().be_empty();
209 }
210
211 #[test]
212 #[should_panic(expected = "Expected collection to have length 5, but it had length 3")]
213 fn have_length_panics_on_mismatch() {
214 vec![1, 2, 3].should().have_length(5);
215 }
216}