| 1 | module cbor |
| 2 | |
| 3 | // Typed errors for CBOR decode failures. Pattern-match in callers: |
| 4 | // |
| 5 | // cbor.decode[User](bad) or { |
| 6 | // if err is cbor.UnexpectedEofError { ... } |
| 7 | // } |
| 8 | |
| 9 | // UnexpectedEofError fires when the decoder runs past the end of its input. |
| 10 | // `need` is i64 so it can represent the full CBOR length range (which is |
| 11 | // u64 on the wire); huge values are clamped to i64::max for reporting. |
| 12 | pub struct UnexpectedEofError { |
| 13 | Error |
| 14 | pub: |
| 15 | pos int // position at which the read began |
| 16 | need i64 // bytes the decoder was trying to read |
| 17 | remaining int // bytes actually available |
| 18 | } |
| 19 | |
| 20 | // msg formats an UnexpectedEofError for `IError.msg()`. |
| 21 | pub fn (e &UnexpectedEofError) msg() string { |
| 22 | if e.need == max_i64 { |
| 23 | return 'cbor: unexpected EOF at pos ${e.pos}: declared length exceeds available input (have ${e.remaining})' |
| 24 | } |
| 25 | return 'cbor: unexpected EOF at pos ${e.pos}: need ${e.need} bytes, have ${e.remaining}' |
| 26 | } |
| 27 | |
| 28 | // MalformedError fires when the byte stream violates RFC 8949 well-formedness. |
| 29 | pub struct MalformedError { |
| 30 | Error |
| 31 | pub: |
| 32 | pos int |
| 33 | reason string |
| 34 | } |
| 35 | |
| 36 | // msg formats a MalformedError for `IError.msg()`. |
| 37 | pub fn (e &MalformedError) msg() string { |
| 38 | return 'cbor: malformed at pos ${e.pos}: ${e.reason}' |
| 39 | } |
| 40 | |
| 41 | // TypeMismatchError fires when a typed read finds a different major type. |
| 42 | pub struct TypeMismatchError { |
| 43 | Error |
| 44 | pub: |
| 45 | pos int |
| 46 | expected string |
| 47 | got u8 // initial byte |
| 48 | } |
| 49 | |
| 50 | // msg formats a TypeMismatchError for `IError.msg()`. |
| 51 | pub fn (e &TypeMismatchError) msg() string { |
| 52 | return 'cbor: type mismatch at pos ${e.pos}: expected ${e.expected}, got initial byte 0x${e.got:02x}' |
| 53 | } |
| 54 | |
| 55 | // MaxDepthError fires when nested arrays/maps exceed the configured cap. |
| 56 | pub struct MaxDepthError { |
| 57 | Error |
| 58 | pub: |
| 59 | pos int |
| 60 | max_depth int |
| 61 | } |
| 62 | |
| 63 | // msg formats a MaxDepthError for `IError.msg()`. |
| 64 | pub fn (e &MaxDepthError) msg() string { |
| 65 | return 'cbor: max nesting depth ${e.max_depth} exceeded at pos ${e.pos}' |
| 66 | } |
| 67 | |
| 68 | // UnknownFieldError fires when a struct decoded with `deny_unknown_fields` |
| 69 | // encounters an unmapped key. |
| 70 | pub struct UnknownFieldError { |
| 71 | Error |
| 72 | pub: |
| 73 | pos int |
| 74 | name string |
| 75 | } |
| 76 | |
| 77 | // msg formats an UnknownFieldError for `IError.msg()`. |
| 78 | pub fn (e &UnknownFieldError) msg() string { |
| 79 | return 'cbor: unknown field "${e.name}" at pos ${e.pos}' |
| 80 | } |
| 81 | |
| 82 | // IntRangeError fires when a decoded integer doesn't fit the target type. |
| 83 | pub struct IntRangeError { |
| 84 | Error |
| 85 | pub: |
| 86 | pos int |
| 87 | target string |
| 88 | value string |
| 89 | } |
| 90 | |
| 91 | // msg formats an IntRangeError for `IError.msg()`. |
| 92 | pub fn (e &IntRangeError) msg() string { |
| 93 | return 'cbor: integer ${e.value} at pos ${e.pos} out of range for ${e.target}' |
| 94 | } |
| 95 | |
| 96 | // InvalidUtf8Error fires when a text-string payload isn't valid UTF-8 and |
| 97 | // the decoder is configured to validate strings. |
| 98 | pub struct InvalidUtf8Error { |
| 99 | Error |
| 100 | pub: |
| 101 | pos int |
| 102 | } |
| 103 | |
| 104 | // msg formats an InvalidUtf8Error for `IError.msg()`. |
| 105 | pub fn (e &InvalidUtf8Error) msg() string { |
| 106 | return 'cbor: invalid UTF-8 in text string at pos ${e.pos}' |
| 107 | } |
| 108 | |
| 109 | @[cold; inline] |
| 110 | fn eof_at(pos int) IError { |
| 111 | return UnexpectedEofError{ |
| 112 | pos: pos |
| 113 | need: 1 |
| 114 | remaining: 0 |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | @[cold; inline] |
| 119 | fn eof_needing(pos int, need i64, remaining int) IError { |
| 120 | return UnexpectedEofError{ |
| 121 | pos: pos |
| 122 | need: need |
| 123 | remaining: remaining |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | // eof_oversized reports an EOF caused by a length argument larger than |
| 128 | // the host can represent, clamped to i64::max so callers see a sensible |
| 129 | // number rather than a negative wrap-around. Used by string/bytes |
| 130 | // chunk readers where the wire length is u64. |
| 131 | @[cold; inline] |
| 132 | fn eof_oversized(pos int, want u64, remaining int) IError { |
| 133 | clamped := if want > u64(max_i64) { max_i64 } else { i64(want) } |
| 134 | return UnexpectedEofError{ |
| 135 | pos: pos |
| 136 | need: clamped |
| 137 | remaining: remaining |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | @[cold; inline] |
| 142 | fn malformed(pos int, reason string) IError { |
| 143 | return MalformedError{ |
| 144 | pos: pos |
| 145 | reason: reason |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | @[cold; inline] |
| 150 | fn type_mismatch(pos int, expected string, got u8) IError { |
| 151 | return TypeMismatchError{ |
| 152 | pos: pos |
| 153 | expected: expected |
| 154 | got: got |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | @[cold; inline] |
| 159 | fn int_range(pos int, target string, value string) IError { |
| 160 | return IntRangeError{ |
| 161 | pos: pos |
| 162 | target: target |
| 163 | value: value |
| 164 | } |
| 165 | } |
| 166 | |