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 }