github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/throw/as.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package throw
     7  
     8  import (
     9  	"errors"
    10  	"reflect"
    11  )
    12  
    13  func asDetail(value interface{}, target interface{}) bool {
    14  	targetType, val := checkAsTarget(target)
    15  	if value == nil {
    16  		return false
    17  	}
    18  	if reflect.TypeOf(value).AssignableTo(targetType) {
    19  		val.Elem().Set(reflect.ValueOf(value))
    20  		return true
    21  	}
    22  	if x, ok := value.(interface{ AsDetail(interface{}) bool }); ok && x.AsDetail(target) {
    23  		return true
    24  	}
    25  	return false
    26  }
    27  
    28  func FindDetail(value error, target interface{}) bool {
    29  	targetType, val := checkAsTarget(target)
    30  	for value != nil {
    31  		switch valueType := reflect.TypeOf(value); {
    32  		case valueType.Kind() == reflect.Ptr:
    33  			if valueType.Elem().AssignableTo(targetType) {
    34  				val.Elem().Set(reflect.ValueOf(value).Elem())
    35  				return true
    36  			}
    37  		case valueType.AssignableTo(targetType):
    38  			val.Elem().Set(reflect.ValueOf(value))
    39  			return true
    40  		}
    41  		if x, ok := value.(interface{ As(interface{}) bool }); ok && x.As(target) {
    42  			return true
    43  		}
    44  		if x, ok := value.(interface{ AsDetail(interface{}) bool }); ok && x.AsDetail(target) {
    45  			return true
    46  		}
    47  		value = errors.Unwrap(value)
    48  	}
    49  	return false
    50  }
    51  
    52  func checkAsTarget(target interface{}) (reflect.Type, reflect.Value) {
    53  	if target == nil {
    54  		panic("errors: target cannot be nil")
    55  	}
    56  	val := reflect.ValueOf(target)
    57  	typ := val.Type()
    58  	if typ.Kind() != reflect.Ptr || val.IsNil() {
    59  		panic("errors: target must be a non-nil pointer")
    60  	}
    61  	targetType := typ.Elem()
    62  	switch targetType.Kind() {
    63  	case reflect.Interface, reflect.Struct:
    64  		return targetType, val
    65  	default:
    66  		panic("errors: *target must be interface or struct")
    67  	}
    68  }