@@ -46,7 +46,9 @@ use std::{
4646 error,
4747 fmt:: { self , Formatter , Write } ,
4848 iter:: { empty, once} ,
49+ slice,
4950 string:: FromUtf8Error ,
51+ vec,
5052} ;
5153
5254/// An error that can occur during validation.
@@ -88,15 +90,145 @@ impl<'a, T> ValidationErrorIterator<'a> for T where
8890{
8991}
9092
91- pub type ErrorIterator < ' a > = Box < dyn ValidationErrorIterator < ' a > + ' a > ;
93+ /// A lazily-evaluated iterator over validation errors.
94+ ///
95+ /// Use [`into_errors()`](Self::into_errors) to convert into [`ValidationErrors`],
96+ /// which implements [`std::error::Error`] for integration with error handling libraries.
97+ pub struct ErrorIterator < ' a > {
98+ iter : Box < dyn ValidationErrorIterator < ' a > + ' a > ,
99+ }
100+
101+ impl < ' a > ErrorIterator < ' a > {
102+ #[ inline]
103+ pub ( crate ) fn from_iterator < T > ( iterator : T ) -> Self
104+ where
105+ T : ValidationErrorIterator < ' a > + ' a ,
106+ {
107+ Self {
108+ iter : Box :: new ( iterator) ,
109+ }
110+ }
111+
112+ /// Collects all errors into [`ValidationErrors`], which implements [`std::error::Error`].
113+ #[ inline]
114+ #[ must_use]
115+ pub fn into_errors ( self ) -> ValidationErrors < ' a > {
116+ ValidationErrors {
117+ errors : self . collect ( ) ,
118+ }
119+ }
120+ }
121+
122+ /// An owned collection of validation errors that implements [`std::error::Error`].
123+ ///
124+ /// Obtain this by calling [`ErrorIterator::into_errors()`].
125+ pub struct ValidationErrors < ' a > {
126+ errors : Vec < ValidationError < ' a > > ,
127+ }
128+
129+ impl < ' a > ValidationErrors < ' a > {
130+ #[ inline]
131+ #[ must_use]
132+ pub fn len ( & self ) -> usize {
133+ self . errors . len ( )
134+ }
135+
136+ #[ inline]
137+ #[ must_use]
138+ pub fn is_empty ( & self ) -> bool {
139+ self . errors . is_empty ( )
140+ }
141+
142+ /// Returns the errors as a slice.
143+ #[ inline]
144+ #[ must_use]
145+ pub fn as_slice ( & self ) -> & [ ValidationError < ' a > ] {
146+ & self . errors
147+ }
148+
149+ #[ inline]
150+ pub fn iter ( & self ) -> slice:: Iter < ' _ , ValidationError < ' a > > {
151+ self . errors . iter ( )
152+ }
153+
154+ #[ inline]
155+ pub fn iter_mut ( & mut self ) -> slice:: IterMut < ' _ , ValidationError < ' a > > {
156+ self . errors . iter_mut ( )
157+ }
158+ }
159+
160+ impl fmt:: Display for ValidationErrors < ' _ > {
161+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
162+ if self . errors . is_empty ( ) {
163+ f. write_str ( "Validation succeeded" )
164+ } else {
165+ writeln ! ( f, "Validation errors:" ) ?;
166+ for ( idx, error) in self . errors . iter ( ) . enumerate ( ) {
167+ writeln ! ( f, "{:02}: {error}" , idx + 1 ) ?;
168+ }
169+ Ok ( ( ) )
170+ }
171+ }
172+ }
173+
174+ impl fmt:: Debug for ValidationErrors < ' _ > {
175+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
176+ f. debug_struct ( "ValidationErrors" )
177+ . field ( "errors" , & self . errors )
178+ . finish ( )
179+ }
180+ }
181+
182+ impl error:: Error for ValidationErrors < ' _ > { }
183+
184+ impl < ' a > Iterator for ErrorIterator < ' a > {
185+ type Item = ValidationError < ' a > ;
186+
187+ #[ inline]
188+ fn next ( & mut self ) -> Option < Self :: Item > {
189+ self . iter . as_mut ( ) . next ( )
190+ }
191+
192+ #[ inline]
193+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
194+ self . iter . size_hint ( )
195+ }
196+ }
197+
198+ impl < ' a > IntoIterator for ValidationErrors < ' a > {
199+ type Item = ValidationError < ' a > ;
200+ type IntoIter = vec:: IntoIter < ValidationError < ' a > > ;
201+
202+ fn into_iter ( self ) -> Self :: IntoIter {
203+ self . errors . into_iter ( )
204+ }
205+ }
206+
207+ impl < ' a , ' b > IntoIterator for & ' b ValidationErrors < ' a > {
208+ type Item = & ' b ValidationError < ' a > ;
209+ type IntoIter = slice:: Iter < ' b , ValidationError < ' a > > ;
210+
211+ fn into_iter ( self ) -> Self :: IntoIter {
212+ self . errors . iter ( )
213+ }
214+ }
215+
216+ impl < ' a , ' b > IntoIterator for & ' b mut ValidationErrors < ' a > {
217+ type Item = & ' b mut ValidationError < ' a > ;
218+ type IntoIter = slice:: IterMut < ' b , ValidationError < ' a > > ;
219+
220+ fn into_iter ( self ) -> Self :: IntoIter {
221+ self . errors . iter_mut ( )
222+ }
223+ }
92224
93225// Empty iterator means no error happened
94226pub ( crate ) fn no_error < ' a > ( ) -> ErrorIterator < ' a > {
95- Box :: new ( empty ( ) )
227+ ErrorIterator :: from_iterator ( empty ( ) )
96228}
97229// A wrapper for one error
98230pub ( crate ) fn error ( instance : ValidationError ) -> ErrorIterator {
99- Box :: new ( once ( instance) )
231+ ErrorIterator :: from_iterator ( once ( instance) )
100232}
101233
102234/// Kinds of errors that may happen during validation
@@ -1395,6 +1527,166 @@ mod tests {
13951527
13961528 use test_case:: test_case;
13971529
1530+ fn owned_error ( instance : Value , kind : ValidationErrorKind ) -> ValidationError < ' static > {
1531+ ValidationError :: new ( Cow :: Owned ( instance) , kind, Location :: new ( ) , Location :: new ( ) )
1532+ }
1533+
1534+ #[ test]
1535+ fn error_iterator_into_errors_collects_all_errors ( ) {
1536+ let iterator = ErrorIterator :: from_iterator (
1537+ vec ! [
1538+ owned_error( json!( 1 ) , ValidationErrorKind :: Minimum { limit: json!( 2 ) } ) ,
1539+ owned_error( json!( 3 ) , ValidationErrorKind :: Maximum { limit: json!( 2 ) } ) ,
1540+ ]
1541+ . into_iter ( ) ,
1542+ ) ;
1543+ let validation_errors = iterator. into_errors ( ) ;
1544+ let collected: Vec < _ > = validation_errors. into_iter ( ) . collect ( ) ;
1545+ assert_eq ! ( collected. len( ) , 2 ) ;
1546+ assert_eq ! ( collected[ 0 ] . to_string( ) , "1 is less than the minimum of 2" ) ;
1547+ assert_eq ! (
1548+ collected[ 1 ] . to_string( ) ,
1549+ "3 is greater than the maximum of 2"
1550+ ) ;
1551+ }
1552+
1553+ #[ test]
1554+ fn validation_errors_display_reports_success ( ) {
1555+ let errors = ValidationErrors { errors : Vec :: new ( ) } ;
1556+ assert_eq ! ( format!( "{errors}" ) , "Validation succeeded" ) ;
1557+ }
1558+
1559+ #[ test]
1560+ fn validation_errors_display_lists_messages ( ) {
1561+ let errors = ValidationErrors {
1562+ errors : vec ! [
1563+ owned_error( json!( 1 ) , ValidationErrorKind :: Minimum { limit: json!( 2 ) } ) ,
1564+ owned_error( json!( 3 ) , ValidationErrorKind :: Maximum { limit: json!( 2 ) } ) ,
1565+ ] ,
1566+ } ;
1567+ let rendered = format ! ( "{errors}" ) ;
1568+ assert ! ( rendered. contains( "Validation errors:" ) ) ;
1569+ assert ! ( rendered. contains( "01: 1 is less than the minimum of 2" ) ) ;
1570+ assert ! ( rendered. contains( "02: 3 is greater than the maximum of 2" ) ) ;
1571+ }
1572+
1573+ #[ test]
1574+ fn validation_errors_len_and_is_empty ( ) {
1575+ let empty = ValidationErrors { errors : vec ! [ ] } ;
1576+ assert_eq ! ( empty. len( ) , 0 ) ;
1577+ assert ! ( empty. is_empty( ) ) ;
1578+
1579+ let errors = ValidationErrors {
1580+ errors : vec ! [ owned_error(
1581+ json!( 1 ) ,
1582+ ValidationErrorKind :: Minimum { limit: json!( 2 ) } ,
1583+ ) ] ,
1584+ } ;
1585+ assert_eq ! ( errors. len( ) , 1 ) ;
1586+ assert ! ( !errors. is_empty( ) ) ;
1587+ }
1588+
1589+ #[ test]
1590+ fn validation_errors_as_slice ( ) {
1591+ let errors = ValidationErrors {
1592+ errors : vec ! [
1593+ owned_error( json!( 1 ) , ValidationErrorKind :: Minimum { limit: json!( 2 ) } ) ,
1594+ owned_error( json!( 3 ) , ValidationErrorKind :: Maximum { limit: json!( 2 ) } ) ,
1595+ ] ,
1596+ } ;
1597+
1598+ let slice = errors. as_slice ( ) ;
1599+ assert_eq ! ( slice. len( ) , 2 ) ;
1600+ assert_eq ! ( slice[ 0 ] . to_string( ) , "1 is less than the minimum of 2" ) ;
1601+ assert_eq ! ( slice[ 1 ] . to_string( ) , "3 is greater than the maximum of 2" ) ;
1602+ }
1603+
1604+ #[ test]
1605+ fn validation_errors_iter ( ) {
1606+ let errors = ValidationErrors {
1607+ errors : vec ! [
1608+ owned_error( json!( 1 ) , ValidationErrorKind :: Minimum { limit: json!( 2 ) } ) ,
1609+ owned_error( json!( 3 ) , ValidationErrorKind :: Maximum { limit: json!( 2 ) } ) ,
1610+ ] ,
1611+ } ;
1612+
1613+ let collected: Vec < _ > = errors. iter ( ) . map ( ValidationError :: to_string) . collect ( ) ;
1614+ assert_eq ! ( collected. len( ) , 2 ) ;
1615+ assert_eq ! ( collected[ 0 ] , "1 is less than the minimum of 2" ) ;
1616+ assert_eq ! ( collected[ 1 ] , "3 is greater than the maximum of 2" ) ;
1617+ }
1618+
1619+ #[ test]
1620+ #[ allow( clippy:: explicit_iter_loop) ]
1621+ fn validation_errors_iter_mut ( ) {
1622+ let mut errors = ValidationErrors {
1623+ errors : vec ! [ owned_error(
1624+ json!( 1 ) ,
1625+ ValidationErrorKind :: Minimum { limit: json!( 2 ) } ,
1626+ ) ] ,
1627+ } ;
1628+
1629+ // Verify we can get mutable references via iter_mut()
1630+ for error in errors. iter_mut ( ) {
1631+ let _ = error. to_string ( ) ;
1632+ }
1633+ }
1634+
1635+ #[ test]
1636+ fn validation_errors_into_iterator_by_ref ( ) {
1637+ let errors = ValidationErrors {
1638+ errors : vec ! [ owned_error(
1639+ json!( 1 ) ,
1640+ ValidationErrorKind :: Minimum { limit: json!( 2 ) } ,
1641+ ) ] ,
1642+ } ;
1643+
1644+ let collected: Vec < _ > = ( & errors) . into_iter ( ) . collect ( ) ;
1645+ assert_eq ! ( collected. len( ) , 1 ) ;
1646+ // Verify errors is still usable
1647+ assert_eq ! ( errors. len( ) , 1 ) ;
1648+ }
1649+
1650+ #[ test]
1651+ fn validation_errors_into_iterator_by_mut_ref ( ) {
1652+ let mut errors = ValidationErrors {
1653+ errors : vec ! [ owned_error(
1654+ json!( 1 ) ,
1655+ ValidationErrorKind :: Minimum { limit: json!( 2 ) } ,
1656+ ) ] ,
1657+ } ;
1658+
1659+ let collected: Vec < _ > = ( & mut errors) . into_iter ( ) . collect ( ) ;
1660+ assert_eq ! ( collected. len( ) , 1 ) ;
1661+ // Verify errors is still usable
1662+ assert_eq ! ( errors. len( ) , 1 ) ;
1663+ }
1664+
1665+ #[ test]
1666+ fn error_iterator_size_hint ( ) {
1667+ let vec = vec ! [
1668+ owned_error( json!( 1 ) , ValidationErrorKind :: Minimum { limit: json!( 2 ) } ) ,
1669+ owned_error( json!( 3 ) , ValidationErrorKind :: Maximum { limit: json!( 2 ) } ) ,
1670+ ] ;
1671+ let iterator = ErrorIterator :: from_iterator ( vec. into_iter ( ) ) ;
1672+ let ( lower, upper) = iterator. size_hint ( ) ;
1673+ assert_eq ! ( lower, 2 ) ;
1674+ assert_eq ! ( upper, Some ( 2 ) ) ;
1675+ }
1676+
1677+ #[ test]
1678+ fn validation_errors_debug ( ) {
1679+ let errors = ValidationErrors {
1680+ errors : vec ! [ owned_error(
1681+ json!( 1 ) ,
1682+ ValidationErrorKind :: Minimum { limit: json!( 2 ) } ,
1683+ ) ] ,
1684+ } ;
1685+ let debug_output = format ! ( "{errors:?}" ) ;
1686+ assert ! ( debug_output. contains( "ValidationErrors" ) ) ;
1687+ assert ! ( debug_output. contains( "errors" ) ) ;
1688+ }
1689+
13981690 #[ test]
13991691 fn single_type_error ( ) {
14001692 let instance = json ! ( 42 ) ;
0 commit comments