github.com/alloyzeus/go-azfl@v0.0.0-20231220071816-9740126a2d07/errors/named.go (about)

     1  package errors
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  type NamedError interface {
     9  	Unwrappable
    10  
    11  	Name() string
    12  	Value() any
    13  }
    14  
    15  type NamedErrorBuilder interface {
    16  	NamedError
    17  
    18  	// Desc returns a copy with descriptor is set to desc.
    19  	Desc(desc ErrorDescriptor) NamedErrorBuilder
    20  
    21  	// DescMsg sets the descriptor with the provided string. For the best
    22  	// experience, descMsg should be defined as a constant so that the error
    23  	// users could use it to identify an error. For non-constant descriptor
    24  	// use the Wrap method.
    25  	DescMsg(descMsg string) NamedErrorBuilder
    26  
    27  	// Hint provides a clue for the developers on how to fix the error.
    28  	Hint(hintText string) NamedErrorBuilder
    29  
    30  	Fieldset(fields ...NamedError) NamedErrorBuilder
    31  
    32  	// Rewrap collects descriptor, wrapped, and fields from err and include
    33  	// them into the new error.
    34  	Rewrap(err error) NamedErrorBuilder
    35  
    36  	// Value provides the value associated to the name.
    37  	Val(value any) NamedErrorBuilder
    38  
    39  	// Wrap returns a copy with wrapped error is set to detailingError.
    40  	Wrap(detailingError error) NamedErrorBuilder
    41  }
    42  
    43  func N(name string) NamedErrorBuilder {
    44  	return &namedError{name: name}
    45  }
    46  
    47  // NamedValueMalformed creates an NamedValue with name is set to
    48  // the value of valueName and descriptor is set to ErrValueMalformed.
    49  func NamedValueMalformed(valueName string) NamedErrorBuilder {
    50  	return &namedError{
    51  		name:       valueName,
    52  		descriptor: ErrValueMalformed,
    53  	}
    54  }
    55  
    56  // NamedValueUnsupported creates an NamedValue with name is set to
    57  // the value of valueName and descriptor is set to ErrValueUnsupported.
    58  func NamedValueUnsupported(valueName string) NamedErrorBuilder {
    59  	return &namedError{
    60  		name:       valueName,
    61  		descriptor: ErrValueUnsupported,
    62  	}
    63  }
    64  
    65  type namedError struct {
    66  	name       string
    67  	descriptor ErrorDescriptor
    68  	hintText   string
    69  	fields     []NamedError
    70  	value      any
    71  	wrapped    error
    72  }
    73  
    74  func (e *namedError) Error() string {
    75  	suffix := namedSetToString(e.fields)
    76  	if suffix != "" {
    77  		suffix = ": " + suffix
    78  	}
    79  	if e.hintText != "" {
    80  		suffix = suffix + ". " + e.hintText
    81  	}
    82  	var descStr string
    83  	if e.descriptor != nil {
    84  		descStr = e.descriptor.Error()
    85  	}
    86  	causeStr := errorString(e.wrapped)
    87  	if causeStr == "" {
    88  		causeStr = descStr
    89  	} else if descStr != "" {
    90  		causeStr = descStr + ": " + causeStr
    91  	}
    92  
    93  	var name string
    94  	if e.name != "" {
    95  		if e.value != nil {
    96  			valStr := fmt.Sprintf("%v", e.value)
    97  			if strings.ContainsAny(valStr, string([]byte{'\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0})) {
    98  				name = fmt.Sprintf("%s=%q", e.name, valStr)
    99  			} else {
   100  				name = fmt.Sprintf("%s=%s", e.name, valStr)
   101  			}
   102  		} else {
   103  			name = e.name
   104  		}
   105  	}
   106  
   107  	if name != "" {
   108  		if causeStr != "" {
   109  			return name + ": " + causeStr + suffix
   110  		}
   111  		return name + suffix
   112  	}
   113  	if causeStr != "" {
   114  		return causeStr + suffix
   115  	}
   116  	if suffix != "" {
   117  		return strings.TrimPrefix(suffix, ": ")
   118  	}
   119  	return ""
   120  }
   121  
   122  func (e namedError) Unwrap() error { return e.wrapped }
   123  func (e namedError) Name() string  { return e.name }
   124  func (e namedError) Value() any    { return e.value }
   125  func (e namedError) Descriptor() ErrorDescriptor {
   126  	if e.descriptor != nil {
   127  		return e.descriptor
   128  	}
   129  	if desc, ok := e.wrapped.(ErrorDescriptor); ok {
   130  		return desc
   131  	}
   132  	return nil
   133  }
   134  func (e namedError) FieldErrors() []NamedError {
   135  	return copyNamedSet(e.fields)
   136  }
   137  
   138  func (e namedError) Desc(desc ErrorDescriptor) NamedErrorBuilder {
   139  	e.descriptor = desc
   140  	return &e
   141  }
   142  
   143  func (e namedError) DescMsg(descMsg string) NamedErrorBuilder {
   144  	e.descriptor = constantErrorDescriptor(descMsg)
   145  	return &e
   146  }
   147  
   148  func (e namedError) Hint(hintText string) NamedErrorBuilder {
   149  	e.hintText = hintText
   150  	return &e
   151  }
   152  
   153  func (e namedError) Fieldset(fields ...NamedError) NamedErrorBuilder {
   154  	e.fields = copyNamedSet(fields)
   155  	return &e
   156  }
   157  
   158  func (e namedError) Rewrap(err error) NamedErrorBuilder {
   159  	if err != nil {
   160  		if descErr, _ := err.(ErrorDescriptor); descErr != nil {
   161  			e.descriptor = descErr
   162  			e.value = nil
   163  			e.wrapped = nil
   164  			e.fields = nil
   165  		} else {
   166  			e.descriptor = UnwrapDescriptor(err)
   167  			e.value = unwrapValue(err)
   168  			e.wrapped = Unwrap(err)
   169  			e.fields = UnwrapFieldErrors(err)
   170  		}
   171  	}
   172  	return &e
   173  }
   174  
   175  func (e namedError) Val(value any) NamedErrorBuilder {
   176  	e.value = value
   177  	return &e
   178  }
   179  
   180  func (e namedError) Wrap(detailingError error) NamedErrorBuilder {
   181  	e.wrapped = detailingError
   182  	return &e
   183  }
   184  
   185  func copyNamedSet(namedSet []NamedError) []NamedError {
   186  	var copiedFields []NamedError
   187  	if len(namedSet) > 0 {
   188  		copiedFields = make([]NamedError, 0, len(namedSet))
   189  		for _, e := range namedSet {
   190  			if e != nil {
   191  				copiedFields = append(copiedFields, e)
   192  			}
   193  		}
   194  	}
   195  	return copiedFields
   196  }
   197  
   198  func namedSetToString(namedSet []NamedError) string {
   199  	if flen := len(namedSet); flen > 0 {
   200  		parts := make([]string, 0, flen)
   201  		for _, sub := range namedSet {
   202  			parts = append(parts, sub.Error())
   203  		}
   204  		return strings.Join(parts, ", ")
   205  	}
   206  	return ""
   207  }
   208  
   209  func unwrapValue(err error) any {
   210  	if err != nil {
   211  		if d, ok := err.(valueHolder); ok {
   212  			return d.Value()
   213  		}
   214  	}
   215  	return nil
   216  }
   217  
   218  type valueHolder interface {
   219  	Value() any
   220  }