@@ -195,42 +195,6 @@ pub mod internal {
195
195
use std:: collections:: HashSet ;
196
196
use std:: fmt:: { Debug , Display } ;
197
197
198
- /// The requirements of the mapping between matchers and actual values by
199
- /// which [`UnorderedElemetnsAre`] is deemed to match its input.
200
- ///
201
- /// **For internal use only. API stablility is not guaranteed!**
202
- #[ doc( hidden) ]
203
- #[ derive( Clone , Copy ) ]
204
- pub enum Requirements {
205
- /// There must be a 1:1 correspondence between the actual values and the
206
- /// matchers.
207
- PerfectMatch ,
208
-
209
- /// The mapping from matched actual values to their corresponding
210
- /// matchers must be surjective.
211
- Superset ,
212
-
213
- /// The mapping from matchers to matched actual values must be
214
- /// surjective.
215
- Subset ,
216
- }
217
-
218
- impl Display for Requirements {
219
- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
220
- match self {
221
- Requirements :: PerfectMatch => {
222
- write ! ( f, "perfect" )
223
- }
224
- Requirements :: Superset => {
225
- write ! ( f, "superset" )
226
- }
227
- Requirements :: Subset => {
228
- write ! ( f, "subset" )
229
- }
230
- }
231
- }
232
- }
233
-
234
198
/// This struct is meant to be used only through the
235
199
/// `unordered_elements_are![...]` macro.
236
200
///
@@ -262,89 +226,30 @@ pub mod internal {
262
226
for < ' b > & ' b ContainerT : IntoIterator < Item = & ' b T > ,
263
227
{
264
228
fn matches ( & self , actual : & ContainerT ) -> MatcherResult {
265
- match self . requirements {
266
- Requirements :: PerfectMatch => {
267
- let match_matrix = MatchMatrix :: generate ( actual, & self . elements ) ;
268
- if !match_matrix. find_unmatchable_elements ( ) . has_unmatchable_elements ( )
269
- && match_matrix. find_best_match ( ) . is_full_match ( )
270
- {
271
- MatcherResult :: Matches
272
- } else {
273
- MatcherResult :: DoesNotMatch
274
- }
275
- }
276
- Requirements :: Superset => {
277
- let match_matrix = MatchMatrix :: generate ( actual, & self . elements ) ;
278
- if !match_matrix. find_unmatched_expected ( ) . has_unmatchable_elements ( )
279
- && match_matrix. find_best_match ( ) . is_superset_match ( )
280
- {
281
- MatcherResult :: Matches
282
- } else {
283
- MatcherResult :: DoesNotMatch
284
- }
285
- }
286
- Requirements :: Subset => {
287
- let match_matrix = MatchMatrix :: generate ( actual, & self . elements ) ;
288
- if !match_matrix. find_unmatched_actual ( ) . has_unmatchable_elements ( )
289
- && match_matrix. find_best_match ( ) . is_subset_match ( )
290
- {
291
- MatcherResult :: Matches
292
- } else {
293
- MatcherResult :: DoesNotMatch
294
- }
295
- }
296
- }
229
+ let match_matrix = MatchMatrix :: generate ( actual, & self . elements ) ;
230
+ match_matrix. is_match_for ( self . requirements ) . into ( )
297
231
}
298
232
299
233
fn explain_match ( & self , actual : & ContainerT ) -> MatchExplanation {
300
- let actual_size = count_elements ( actual) ;
301
- match self . requirements {
302
- Requirements :: PerfectMatch => {
303
- if actual_size != N {
304
- return MatchExplanation :: create ( format ! (
305
- "which has size {} (expected {})" ,
306
- actual_size, N
307
- ) ) ;
308
- }
309
- }
310
-
311
- Requirements :: Superset => {
312
- if actual_size < N {
313
- return MatchExplanation :: create ( format ! (
314
- "which has size {} (expected at least {})" ,
315
- actual_size, N
316
- ) ) ;
317
- }
318
- }
319
-
320
- Requirements :: Subset => {
321
- if actual_size > N {
322
- return MatchExplanation :: create ( format ! (
323
- "which has size {} (expected at most {})" ,
324
- actual_size, N
325
- ) ) ;
326
- }
327
- }
234
+ if let Some ( size_mismatch_explanation) =
235
+ self . requirements . explain_size_mismatch ( actual, N )
236
+ {
237
+ return size_mismatch_explanation;
328
238
}
329
239
330
240
let match_matrix = MatchMatrix :: generate ( actual, & self . elements ) ;
331
- let unmatchable_elements = match self . requirements {
332
- Requirements :: PerfectMatch => match_matrix. find_unmatchable_elements ( ) ,
333
- Requirements :: Superset => match_matrix. find_unmatched_expected ( ) ,
334
- Requirements :: Subset => match_matrix. find_unmatched_actual ( ) ,
335
- } ;
336
- if let Some ( unmatchable_explanation) = unmatchable_elements. get_explanation ( ) {
337
- return MatchExplanation :: create ( unmatchable_explanation) ;
241
+ if let Some ( unmatchable_explanation) =
242
+ match_matrix. explain_unmatchable ( self . requirements )
243
+ {
244
+ return unmatchable_explanation;
338
245
}
339
246
340
247
let best_match = match_matrix. find_best_match ( ) ;
341
- if let Some ( best_match_explanation) =
342
- best_match. get_explanation ( actual, & self . elements , self . requirements )
343
- {
344
- MatchExplanation :: create ( best_match_explanation)
345
- } else {
346
- MatchExplanation :: create ( "whose elements all match" . to_string ( ) )
347
- }
248
+ MatchExplanation :: create (
249
+ best_match
250
+ . get_explanation ( actual, & self . elements , self . requirements )
251
+ . unwrap_or ( "whose elements all match" . to_string ( ) ) ,
252
+ )
348
253
}
349
254
350
255
fn describe ( & self , matcher_result : MatcherResult ) -> String {
@@ -361,6 +266,79 @@ pub mod internal {
361
266
}
362
267
}
363
268
269
+ /// The requirements of the mapping between matchers and actual values by
270
+ /// which [`UnorderedElemetnsAre`] is deemed to match its input.
271
+ ///
272
+ /// **For internal use only. API stablility is not guaranteed!**
273
+ #[ doc( hidden) ]
274
+ #[ derive( Clone , Copy ) ]
275
+ pub enum Requirements {
276
+ /// There must be a 1:1 correspondence between the actual values and the
277
+ /// matchers.
278
+ PerfectMatch ,
279
+
280
+ /// The mapping from matched actual values to their corresponding
281
+ /// matchers must be surjective.
282
+ Superset ,
283
+
284
+ /// The mapping from matchers to matched actual values must be
285
+ /// surjective.
286
+ Subset ,
287
+ }
288
+
289
+ impl Requirements {
290
+ fn explain_size_mismatch < ContainerT : ?Sized > (
291
+ & self ,
292
+ actual : & ContainerT ,
293
+ expected_size : usize ,
294
+ ) -> Option < MatchExplanation >
295
+ where
296
+ for < ' b > & ' b ContainerT : IntoIterator ,
297
+ {
298
+ let actual_size = count_elements ( actual) ;
299
+ match self {
300
+ Requirements :: PerfectMatch if actual_size != expected_size => {
301
+ Some ( MatchExplanation :: create ( format ! (
302
+ "which has size {} (expected {})" ,
303
+ actual_size, expected_size
304
+ ) ) )
305
+ }
306
+
307
+ Requirements :: Superset if actual_size < expected_size => {
308
+ Some ( MatchExplanation :: create ( format ! (
309
+ "which has size {} (expected at least {})" ,
310
+ actual_size, expected_size
311
+ ) ) )
312
+ }
313
+
314
+ Requirements :: Subset if actual_size > expected_size => {
315
+ Some ( MatchExplanation :: create ( format ! (
316
+ "which has size {} (expected at most {})" ,
317
+ actual_size, expected_size
318
+ ) ) )
319
+ }
320
+
321
+ _ => None ,
322
+ }
323
+ }
324
+ }
325
+
326
+ impl Display for Requirements {
327
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
328
+ match self {
329
+ Requirements :: PerfectMatch => {
330
+ write ! ( f, "perfect" )
331
+ }
332
+ Requirements :: Superset => {
333
+ write ! ( f, "superset" )
334
+ }
335
+ Requirements :: Subset => {
336
+ write ! ( f, "subset" )
337
+ }
338
+ }
339
+ }
340
+ }
341
+
364
342
/// The bipartite matching graph between actual and expected elements.
365
343
struct MatchMatrix < const N : usize > ( Vec < [ MatcherResult ; N ] > ) ;
366
344
@@ -382,6 +360,32 @@ pub mod internal {
382
360
matrix
383
361
}
384
362
363
+ fn is_match_for ( & self , requirements : Requirements ) -> bool {
364
+ match requirements {
365
+ Requirements :: PerfectMatch => {
366
+ !self . find_unmatchable_elements ( ) . has_unmatchable_elements ( )
367
+ && self . find_best_match ( ) . is_full_match ( )
368
+ }
369
+ Requirements :: Superset => {
370
+ !self . find_unmatched_expected ( ) . has_unmatchable_elements ( )
371
+ && self . find_best_match ( ) . is_superset_match ( )
372
+ }
373
+ Requirements :: Subset => {
374
+ !self . find_unmatched_actual ( ) . has_unmatchable_elements ( )
375
+ && self . find_best_match ( ) . is_subset_match ( )
376
+ }
377
+ }
378
+ }
379
+
380
+ fn explain_unmatchable ( & self , requirements : Requirements ) -> Option < MatchExplanation > {
381
+ let unmatchable_elements = match requirements {
382
+ Requirements :: PerfectMatch => self . find_unmatchable_elements ( ) ,
383
+ Requirements :: Superset => self . find_unmatched_expected ( ) ,
384
+ Requirements :: Subset => self . find_unmatched_actual ( ) ,
385
+ } ;
386
+ unmatchable_elements. get_explanation ( ) . map ( MatchExplanation :: create)
387
+ }
388
+
385
389
// Verifies that each actual matches at least one expected and that
386
390
// each expected matches at least one actual.
387
391
// This is a necessary condition but not sufficient. But it is faster
0 commit comments