@@ -167,46 +167,61 @@ func TryValidate(v any) (err error, isValidatable bool) {
167167 }
168168}
169169
170- // DeepValidate validates all fields of a struct, all elements of a slice or array,
170+ // DeepValidate recursively validates all fields of a struct, all elements of a slice or array,
171171// and all values of a map by recursively calling Validate or Valid methods.
172- // It provides detailed error information including the path to invalid values.
173- func DeepValidate (v any ) error {
174- return deepValidate (reflect .ValueOf (v ))
172+ // It returns all validation errors as a slice.
173+ // Use errors.Join(DeepValidate(v)...) to join the errors into a single error.
174+ func DeepValidate (v any ) []error {
175+ var errs []error
176+ deepValidate (reflect .ValueOf (v ), func (err error ) {
177+ errs = append (errs , err )
178+ })
179+ return errs
175180}
176181
177182// deepValidate is the internal implementation of DeepValidate.
178183// It recursively validates nested structures and provides path information for errors.
179- func deepValidate (v reflect.Value , path ... string ) error {
180- err := Validate ( v . Interface () )
181- if err != nil && len ( path ) > 0 {
182- err = fmt . Errorf ( "%s: %w" , strings . Join ( path , " -> " ), err )
184+ func deepValidate (v reflect.Value , onError func ( error ), path ... string ) {
185+ // Handle invalid/zero reflect.Values (e.g., from nil interface{} )
186+ if ! v . IsValid () {
187+ return
183188 }
184- if v .Kind () == reflect .Ptr {
185- if v .IsNil () {
186- return err
189+
190+ // Handle nil pointers before calling v.Interface()
191+ if v .Kind () == reflect .Pointer && v .IsNil () {
192+ return
193+ }
194+
195+ err := Validate (v .Interface ())
196+ if err != nil {
197+ if len (path ) > 0 {
198+ err = fmt .Errorf ("%s: %w" , strings .Join (path , " -> " ), err )
187199 }
200+ onError (err )
201+ }
202+ if v .Kind () == reflect .Pointer {
188203 v = v .Elem ()
189204 }
190205 switch v .Kind () {
191206 case reflect .Struct :
192- for i := 0 ; i < v .NumField (); i ++ {
193- name := fmt .Sprintf ("struct field %s" , v .Type ().Field (i ).Name )
194- err = errors .Join (err , deepValidate (v .Field (i ), append (path , name )... ))
207+ t := v .Type ()
208+ for i := range v .NumField () {
209+ name := fmt .Sprintf ("struct field %s" , t .Field (i ).Name )
210+ deepValidate (v .Field (i ), onError , append (path , name )... )
195211 }
196212 case reflect .Map :
197213 keys := v .MapKeys ()
198214 slices .SortFunc (keys , ReflectCompare )
199215 for _ , key := range keys {
200216 name := fmt .Sprintf ("map value [%#v]" , key .Interface ())
201- err = errors . Join ( err , deepValidate (v .MapIndex (key ), append (path , name )... ) )
217+ deepValidate (v .MapIndex (key ), onError , append (path , name )... )
202218 }
203219 case reflect .Slice , reflect .Array :
204220 for i := 0 ; i < v .Len (); i ++ {
205221 name := fmt .Sprintf ("elememt [%d]" , i )
206- err = errors . Join ( err , deepValidate (v .Index (i ), append (path , name )... ) )
222+ deepValidate (v .Index (i ), onError , append (path , name )... )
207223 }
208224 }
209- return err
210225}
211226
212227// ReflectCompare compares two reflect.Values of the same type.
0 commit comments