github.com/ipld/go-ipld-prime@v0.21.0/schema/errors.go (about)

     1  package schema
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/ipld/go-ipld-prime/datamodel"
     8  )
     9  
    10  // TODO: errors in this package remain somewhat slapdash.
    11  //
    12  //  - datamodel.ErrUnmatchable is used as a catch-all in some places, and contains who-knows-what values wrapped in the Reason field.
    13  //    - sometimes this wraps things like strconv errors... and on the one hand, i'm kinda okay with that; on the other, maybe saying a bit more with types before getting to that kind of shrug would be nice.
    14  //  - we probably want to use `Type` values, right?
    15  //    - or do we: because then we probably need a `Repr bool` next to it, or lots of messages would be nonsensical.
    16  //    - this is *currently* problematic because we don't actually generate type info consts yet.  Hopefully soon; but the pain, meanwhile, is... substantial.
    17  //      - "substantial" is an understatement.  it makes incremental development almost impossible because stringifying error reports turn into nil pointer crashes!
    18  //    - other ipld-wide errors like `datamodel.ErrWrongKind` *sometimes* refer to a TypeName... but don't *have* to, because they also arise at the merely-datamodel level; what would we do with these?
    19  //      - it's undesirable (not to mention intensely forbidden for import cycle reasons) for those error types to refer to schema.Type.
    20  //        - if we must have TypeName treated stringily in some cases, is it really useful to use full type info in other cases -- inconsistently?
    21  //    - regardless of where we end up with this, some sort of an embed for helping deal with munging and printing this would probably be wise.
    22  //  - generally, whether you should expect an "datamodel.Err*" or a "schema.Err*" from various methods is quite unclear.
    23  //  - it's possible that we should wrap *all* schema-level errors in a single "datamodel.ErrSchemaNoMatch" error of some kind, to fix the above.  (and maybe that's what ErrUnmatchable really is.)  as yet undecided.
    24  
    25  // ErrUnmatchable is the error raised when processing data with IPLD Schemas and
    26  // finding data which cannot be matched into the schema.
    27  // It will be returned by NodeAssemblers and NodeBuilders when they are fed unmatchable data.
    28  // As a result, it will also often be seen returned from unmarshalling
    29  // when unmarshalling into schema-constrained NodeAssemblers.
    30  //
    31  // ErrUnmatchable provides the name of the type in the schema that data couldn't be matched to,
    32  // and wraps another error as the more detailed reason.
    33  type ErrUnmatchable struct {
    34  	// TypeName will indicate the named type of a node the function was called on.
    35  	TypeName string
    36  
    37  	// Reason must always be present.  ErrUnmatchable doesn't say much otherwise.
    38  	Reason error
    39  }
    40  
    41  func (e ErrUnmatchable) Error() string {
    42  	return fmt.Sprintf("matching data to schema of %s rejected: %s", e.TypeName, e.Reason)
    43  }
    44  
    45  // Reasonf returns a new ErrUnmatchable with a Reason field set to the Errorf of the arguments.
    46  // It's a helper function for creating untyped error reasons without importing the fmt package.
    47  func (e ErrUnmatchable) Reasonf(format string, a ...interface{}) ErrUnmatchable {
    48  	return ErrUnmatchable{e.TypeName, fmt.Errorf(format, a...)}
    49  }
    50  
    51  // Is provides support for Go's standard errors.Is function so that
    52  // errors.Is(yourError, ErrUnmatchable) may be used to match the type of error.
    53  func (e ErrUnmatchable) Is(err error) bool {
    54  	_, ok := err.(ErrUnmatchable)
    55  	return ok
    56  }
    57  
    58  // ErrMissingRequiredField is returned when calling 'Finish' on a NodeAssembler
    59  // for a Struct that has not has all required fields set.
    60  type ErrMissingRequiredField struct {
    61  	Missing []string
    62  }
    63  
    64  func (e ErrMissingRequiredField) Error() string {
    65  	return "missing required fields: " + strings.Join(e.Missing, ",")
    66  }
    67  
    68  // Is provides support for Go's standard errors.Is function so that
    69  // errors.Is(yourError, ErrMissingRequiredField) may be used to match the type of error.
    70  func (e ErrMissingRequiredField) Is(err error) bool {
    71  	_, ok := err.(ErrMissingRequiredField)
    72  	return ok
    73  }
    74  
    75  // ErrInvalidKey indicates a key is invalid for some reason.
    76  //
    77  // This is only possible for typed nodes; specifically, it may show up when
    78  // handling struct types, or maps with interesting key types.
    79  // (Other kinds of key invalidity that happen for untyped maps
    80  // fall under ErrRepeatedMapKey or ErrWrongKind.)
    81  // (Union types use ErrInvalidUnionDiscriminant instead of ErrInvalidKey,
    82  // even when their representation strategy is maplike.)
    83  type ErrInvalidKey struct {
    84  	// TypeName will indicate the named type of a node the function was called on.
    85  	TypeName string
    86  
    87  	// Key is the key that was rejected.
    88  	Key datamodel.Node
    89  
    90  	// Reason, if set, may provide details (for example, the reason a key couldn't be converted to a type).
    91  	// If absent, it'll be presumed "no such field".
    92  	// ErrUnmatchable may show up as a reason for typed maps with complex keys.
    93  	Reason error
    94  }
    95  
    96  func (e ErrInvalidKey) Error() string {
    97  	if e.Reason == nil {
    98  		return fmt.Sprintf("invalid key for map %s: %q: no such field", e.TypeName, e.Key)
    99  	} else {
   100  		return fmt.Sprintf("invalid key for map %s: %q: %s", e.TypeName, e.Key, e.Reason)
   101  	}
   102  }
   103  
   104  // Is provides support for Go's standard errors.Is function so that
   105  // errors.Is(yourError, ErrInvalidKey) may be used to match the type of error.
   106  func (e ErrInvalidKey) Is(err error) bool {
   107  	_, ok := err.(ErrInvalidKey)
   108  	return ok
   109  }
   110  
   111  // ErrNoSuchField may be returned from lookup functions on the Node
   112  // interface when a field is requested which doesn't exist,
   113  // or from assigning data into on a MapAssembler for a struct
   114  // when the key doesn't match a field name in the structure
   115  // (or, when assigning data into a ListAssembler and the list size has
   116  // reached out of bounds, in case of a struct with list-like representations!).
   117  type ErrNoSuchField struct {
   118  	Type Type
   119  
   120  	Field datamodel.PathSegment
   121  }
   122  
   123  func (e ErrNoSuchField) Error() string {
   124  	if e.Type == nil {
   125  		return fmt.Sprintf("no such field: {typeinfomissing}.%s", e.Field)
   126  	}
   127  	return fmt.Sprintf("no such field: %s.%s", e.Type.Name(), e.Field)
   128  }
   129  
   130  // Is provides support for Go's standard errors.Is function so that
   131  // errors.Is(yourError, ErrNoSuchField) may be used to match the type of error.
   132  func (e ErrNoSuchField) Is(err error) bool {
   133  	_, ok := err.(ErrNoSuchField)
   134  	return ok
   135  }
   136  
   137  // ErrNotUnionStructure means data was fed into a union assembler that can't match the union.
   138  //
   139  // This could have one of several reasons, which are explained in the detail text:
   140  //
   141  //   - there are too many entries in the map;
   142  //   - the keys of critical entries aren't found;
   143  //   - keys are found that aren't any of the expected critical keys;
   144  //   - etc.
   145  //
   146  // TypeName is currently a string... see comments at the top of this file for
   147  // remarks on the issues we need to address about these identifiers in errors in general.
   148  type ErrNotUnionStructure struct {
   149  	TypeName string
   150  
   151  	Detail string
   152  }
   153  
   154  func (e ErrNotUnionStructure) Error() string {
   155  	return fmt.Sprintf("cannot match schema: union structure constraints for %s caused rejection: %s", e.TypeName, e.Detail)
   156  }
   157  
   158  // Is provides support for Go's standard errors.Is function so that
   159  // errors.Is(yourError, ErrNotUnionStructure) may be used to match the type of error.
   160  func (e ErrNotUnionStructure) Is(err error) bool {
   161  	_, ok := err.(ErrNotUnionStructure)
   162  	return ok
   163  }