@@ -184,6 +184,14 @@ impl<'a, O, S> DerivedSpec<'a, O, S> {
184184 /// method to switch back to the previous subject before calling
185185 /// `extracting_ref` for the other property.
186186 ///
187+ /// The expression for failure reports is built from the expression of the
188+ /// original subject, a dot as a separator, and the given property name.
189+ /// The resulting expression is equal to
190+ /// `format!("{original_expression}.{property_name}")`.
191+ ///
192+ /// If you do not like the default built expression, it can be overwritten
193+ /// by calling the `named` method after the call to the `extract` method.
194+ ///
187195 /// # Arguments
188196 ///
189197 /// * `property_name` - A name describing the extracted property used for
@@ -299,12 +307,14 @@ impl<'a, O, S> DerivedSpec<'a, O, S> {
299307 F : FnOnce ( & S ) -> & B ,
300308 B : ToOwned < Owned = U > + ?Sized ,
301309 {
302- let extracted = extract ( & self . subject ) . to_owned ( ) ;
303- let expression = Expression ( property_name. into ( ) ) ;
310+ let derived_subject = extract ( & self . subject ) . to_owned ( ) ;
311+ let orig_subject_name = & self . expression ;
312+ let property_name = property_name. into ( ) ;
313+ let expression = Expression ( format ! ( "{orig_subject_name}.{property_name}" ) . into ( ) ) ;
304314 let diff_format = self . diff_format . clone ( ) ;
305315 DerivedSpec {
306316 original : self ,
307- subject : extracted ,
317+ subject : derived_subject ,
308318 expression,
309319 diff_format,
310320 }
@@ -330,15 +340,25 @@ impl<'a, O, S> DerivedSpec<'a, O, S> {
330340 /// that the "extracted" property is most likely a different subject than
331341 /// the original one.
332342 ///
333- /// It is recommended to give the extracted property a specific name by
334- /// calling the `named` method. This helps with spotting the cause of a
335- /// failing assertion.
343+ /// The expression for failure reports is built from the expression of the
344+ /// original subject, a dot as a separator, and the given property name.
345+ /// The resulting expression is equal to
346+ /// `format!("{original_expression}.{property_name}")`.
347+ ///
348+ /// If you do not like the default built expression, it can be overwritten
349+ /// by calling the `named` method after the call to the `extract` method.
336350 ///
337351 /// This method does not memorize the current subject. Calling `and` on the
338352 /// extracted property switches back to the original subject of this
339353 /// `DerivedSpec`. The current subject is omitted. So, `and` always switches
340354 /// back to the subject before the last `extracting_ref` call.
341355 ///
356+ /// # Arguments
357+ ///
358+ /// * `property_name` - A name describing the extracted property used for
359+ /// referencing this property in failure reports.
360+ /// * `extract` - A closure that returns the property to be extracted.
361+ ///
342362 /// # Example
343363 ///
344364 /// ```
@@ -374,26 +394,33 @@ impl<'a, O, S> DerivedSpec<'a, O, S> {
374394 ///
375395 /// assert_that!(my_order)
376396 /// .extracting_ref("items", |o| &o.items)
377- /// .extracting(|i| i[0].name.clone())
397+ /// .extracting("name", |i| i[0].name.clone())
378398 /// .is_equal_to("Apple")
379399 /// .and() // switches back to `my_order` not `my_order.items`
380- /// .extracting(|o| o.id)
400+ /// .extracting("id", |o| o.id)
381401 /// .is_equal_to("O261234");
382402 /// ```
383403 ///
384404 /// [`extracting_ref`]: Self::extracting_ref
385405 /// [`mapping`]: Self::mapping
386406 #[ must_use = "a derived spec does nothing unless an assertion method is called" ]
387- pub fn extracting < F , U > ( self , extract : F ) -> DerivedSpec < ' a , O , U >
407+ pub fn extracting < F , U > (
408+ self ,
409+ property_name : impl Into < Cow < ' a , str > > ,
410+ extract : F ,
411+ ) -> DerivedSpec < ' a , O , U >
388412 where
389413 F : FnOnce ( S ) -> U ,
390414 {
391- let extracted = extract ( self . subject ) ;
415+ let derived_subject = extract ( self . subject ) ;
416+ let orig_subject_name = & self . expression ;
417+ let property_name = property_name. into ( ) ;
418+ let expression = Expression ( format ! ( "{orig_subject_name}.{property_name}" ) . into ( ) ) ;
392419 let diff_format = self . diff_format . clone ( ) ;
393420 DerivedSpec {
394421 original : self . original ,
395- subject : extracted ,
396- expression : Expression :: default ( ) ,
422+ subject : derived_subject ,
423+ expression,
397424 diff_format,
398425 }
399426 }
@@ -413,7 +440,8 @@ impl<'a, O, S> DerivedSpec<'a, O, S> {
413440 /// `DerivedSpec` also provides the [`extracting()`](DerivedSpec::extracting)
414441 /// method, which is similar to this method. In contrast to this method,
415442 /// [`extracting()`](DerivedSpec::extracting) does not copy the subject's
416- /// name (or expression) but resets it to the default "subject".
443+ /// name (or expression) but builds a new expression from the expression of
444+ /// the original subject and the given property name.
417445 ///
418446 /// # Example
419447 ///
@@ -1567,7 +1595,10 @@ where
15671595 PanicOnFail . do_fail_with ( & spec. failures ( ) ) ;
15681596 unreachable ! ( "Assertion failed and should have panicked! Please report a bug." )
15691597 }
1570- spec. extracting ( |mut collection| collection. remove ( 0 ) )
1598+ let orig_subject_name = spec. expression ( ) ;
1599+ let new_subject_name = format ! ( "the first element of {orig_subject_name}" ) ;
1600+ spec. extracting ( "" , |mut collection| collection. remove ( 0 ) )
1601+ . named ( new_subject_name)
15711602 }
15721603
15731604 fn last_element ( self ) -> Self :: SingleElement {
@@ -1578,11 +1609,14 @@ where
15781609 PanicOnFail . do_fail_with ( & spec. failures ( ) ) ;
15791610 unreachable ! ( "Assertion failed and should have panicked! Please report a bug." )
15801611 }
1581- spec. extracting ( |mut collection| {
1612+ let orig_subject_name = spec. expression ( ) ;
1613+ let new_subject_name = format ! ( "the last element of {orig_subject_name}" ) ;
1614+ spec. extracting ( "" , |mut collection| {
15821615 collection. pop ( ) . unwrap_or_else ( || {
15831616 unreachable ! ( "Assertion failed and should have panicked! Please report a bug." )
15841617 } )
15851618 } )
1619+ . named ( new_subject_name)
15861620 }
15871621
15881622 fn nth_element ( self , n : usize ) -> Self :: SingleElement {
@@ -1594,10 +1628,16 @@ where
15941628 PanicOnFail . do_fail_with ( & spec. failures ( ) ) ;
15951629 unreachable ! ( "Assertion failed and should have panicked! Please report a bug." )
15961630 }
1597- spec. extracting ( |mut collection| collection. remove ( n) )
1631+ let orig_subject_name = spec. expression ( ) ;
1632+ let new_subject_name = format ! ( "{orig_subject_name}[{n}]" ) ;
1633+ spec. extracting ( "" , |mut collection| collection. remove ( n) )
1634+ . named ( new_subject_name)
15981635 }
15991636
16001637 fn elements_at ( self , indices : impl IntoIterator < Item = usize > ) -> Self :: MultipleElements {
1638+ let indices = Vec :: from_iter ( indices) ;
1639+ let orig_subject_name = self . expression ( ) ;
1640+ let new_subject_name = format ! ( "{orig_subject_name} at positions {indices:?}" ) ;
16011641 let indices = HashSet :: < _ > :: from_iter ( indices) ;
16021642 self . mapping ( |subject| {
16031643 subject
@@ -1606,6 +1646,7 @@ where
16061646 . filter_map ( |( i, v) | if indices. contains ( & i) { Some ( v) } else { None } )
16071647 . collect ( )
16081648 } )
1649+ . named ( new_subject_name)
16091650 }
16101651}
16111652
@@ -1706,12 +1747,12 @@ where
17061747 }
17071748 let orig_subject_name = original_spec. expression ( ) ;
17081749 let new_subject_name = format ! ( "the first element of {orig_subject_name}" ) ;
1709- original_spec. extracting_ref ( new_subject_name , |collection|
1750+ original_spec. extracting_ref ( "" , |collection|
17101751 collection. first ( )
17111752 . unwrap_or_else ( ||
17121753 unreachable ! ( "We should have asserted before, that there is at least one element in the collection/iterator. Please file a bug." )
17131754 )
1714- )
1755+ ) . named ( new_subject_name )
17151756 }
17161757
17171758 fn last_element_ref ( self ) -> Self :: SingleElement {
@@ -1724,12 +1765,12 @@ where
17241765 }
17251766 let orig_subject_name = original_spec. expression ( ) ;
17261767 let new_subject_name = format ! ( "the last element of {orig_subject_name}" ) ;
1727- original_spec. extracting_ref ( new_subject_name , |collection|
1768+ original_spec. extracting_ref ( "" , |collection|
17281769 collection. last ( )
17291770 . unwrap_or_else ( ||
17301771 unreachable ! ( "We should have asserted before, that there is at least one element in the collection/iterator. Please file a bug." )
17311772 )
1732- )
1773+ ) . named ( new_subject_name )
17331774 }
17341775
17351776 fn nth_element_ref ( self , n : usize ) -> Self :: SingleElement {
@@ -1743,12 +1784,12 @@ where
17431784 }
17441785 let orig_subject_name = original_spec. expression ( ) ;
17451786 let new_subject_name = format ! ( "{orig_subject_name}[{n}]" ) ;
1746- original_spec. extracting_ref ( new_subject_name , |collection|
1787+ original_spec. extracting_ref ( "" , |collection|
17471788 collection. get ( n)
17481789 . unwrap_or_else ( ||
17491790 unreachable ! ( "We should have asserted before, that there is at least one element in the collection/iterator. Please file a bug." )
17501791 )
1751- )
1792+ ) . named ( new_subject_name )
17521793 }
17531794
17541795 fn elements_ref_at ( self , indices : impl IntoIterator < Item = usize > ) -> Self :: MultipleElements {
@@ -1757,18 +1798,20 @@ where
17571798 let new_subject_name = format ! ( "{orig_subject_name} at positions {indices:?}" ) ;
17581799 let indices = HashSet :: < _ > :: from_iter ( indices) ;
17591800 let original_spec = self . mapping ( Vec :: from_iter) ;
1760- original_spec. extracting_ref_iter ( new_subject_name, |collection| {
1761- collection
1762- . enumerate ( )
1763- . filter_map ( |( i, e) | {
1764- if indices. contains ( & i) {
1765- Some ( e. to_owned ( ) )
1766- } else {
1767- None
1768- }
1769- } )
1770- . collect ( )
1771- } )
1801+ original_spec
1802+ . extracting_ref_iter ( "" , |collection| {
1803+ collection
1804+ . enumerate ( )
1805+ . filter_map ( |( i, e) | {
1806+ if indices. contains ( & i) {
1807+ Some ( e. to_owned ( ) )
1808+ } else {
1809+ None
1810+ }
1811+ } )
1812+ . collect ( )
1813+ } )
1814+ . named ( new_subject_name)
17721815 }
17731816}
17741817
0 commit comments