Add support for decode failures by returning Result type
This commit is contained in:
parent
319418cab7
commit
a43c7b21a4
|
@ -1,6 +1,7 @@
|
|||
import pkg/stint
|
||||
import pkg/stew/endians2
|
||||
import pkg/stew/byteutils
|
||||
import pkg/questionable/results
|
||||
import pkg/upraises
|
||||
import ./encoding
|
||||
|
||||
|
@ -20,7 +21,7 @@ type
|
|||
padRight
|
||||
UInt = SomeUnsignedInt | StUint
|
||||
|
||||
func read*(decoder: var AbiDecoder, T: type): T
|
||||
func read*(decoder: var AbiDecoder, T: type): ?!T
|
||||
|
||||
func init(_: type Tuple, offset: int, dynamic: bool): Tuple =
|
||||
Tuple(start: offset, index: offset, dynamic: dynamic)
|
||||
|
@ -41,55 +42,60 @@ func updateLast(decoder: var AbiDecoder, index: int) =
|
|||
if index > decoder.last:
|
||||
decoder.last = index
|
||||
|
||||
func advance(decoder: var AbiDecoder, amount: int) =
|
||||
func advance(decoder: var AbiDecoder, amount: int): ?!void =
|
||||
decoder.index += amount
|
||||
decoder.updateLast(decoder.index)
|
||||
success()
|
||||
|
||||
func read(decoder: var AbiDecoder, amount: int, padding = padLeft): seq[byte] =
|
||||
func read(decoder: var AbiDecoder, amount: int, padding=padLeft): ?!seq[byte] =
|
||||
let padlen = (32 - amount mod 32) mod 32
|
||||
if padding == padLeft:
|
||||
decoder.advance(padlen)
|
||||
result = decoder.bytes[decoder.index..<decoder.index+amount]
|
||||
decoder.advance(amount)
|
||||
?decoder.advance(padlen)
|
||||
let index = decoder.index
|
||||
?decoder.advance(amount)
|
||||
result = success decoder.bytes[index..<index+amount]
|
||||
if padding == padRight:
|
||||
decoder.advance(padlen)
|
||||
?decoder.advance(padlen)
|
||||
|
||||
func decode(decoder: var AbiDecoder, T: type UInt): T =
|
||||
T.fromBytesBE(decoder.read(sizeof(T)))
|
||||
func decode(decoder: var AbiDecoder, T: type UInt): ?!T =
|
||||
success T.fromBytesBE(?decoder.read(sizeof(T)))
|
||||
|
||||
func decode(decoder: var AbiDecoder, T: type bool): T =
|
||||
decoder.read(uint8) != 0
|
||||
func decode(decoder: var AbiDecoder, T: type bool): ?!T =
|
||||
success (?decoder.read(uint8) != 0)
|
||||
|
||||
func decode(decoder: var AbiDecoder, T: type enum): T =
|
||||
T(decoder.read(uint64))
|
||||
func decode(decoder: var AbiDecoder, T: type enum): ?!T =
|
||||
success T(?decoder.read(uint64))
|
||||
|
||||
func decode[I](decoder: var AbiDecoder, T: type array[I, byte]): T =
|
||||
result[0..<result.len] = decoder.read(result.len, padRight)
|
||||
func decode[I](decoder: var AbiDecoder, T: type array[I, byte]): ?!T =
|
||||
var arr: T
|
||||
arr[0..<arr.len] = ?decoder.read(arr.len, padRight)
|
||||
success arr
|
||||
|
||||
func readOffset(decoder: var AbiDecoder): int =
|
||||
let offset = decoder.read(uint64)
|
||||
decoder.currentTuple.start + offset.int
|
||||
func readOffset(decoder: var AbiDecoder): ?!int =
|
||||
let offset = ?decoder.read(uint64)
|
||||
success decoder.currentTuple.start + offset.int
|
||||
|
||||
func readTail(decoder: var AbiDecoder, T: type seq[byte]): T =
|
||||
let offset = decoder.readOffset()
|
||||
func readTail(decoder: var AbiDecoder): ?!seq[byte] =
|
||||
let offset = ?decoder.readOffset()
|
||||
var tailDecoder = AbiDecoder.init(decoder.bytes, offset)
|
||||
result = tailDecoder.read(T)
|
||||
result = tailDecoder.read(seq[byte])
|
||||
decoder.updateLast(tailDecoder.index)
|
||||
|
||||
func decode(decoder: var AbiDecoder, T: type seq[byte]): T =
|
||||
func decode(decoder: var AbiDecoder, T: type seq[byte]): ?!T =
|
||||
if decoder.currentTuple.dynamic:
|
||||
decoder.readTail(T)
|
||||
decoder.readTail()
|
||||
else:
|
||||
let len = decoder.read(uint64).int
|
||||
decoder.read(len, padRight)
|
||||
let len = ?decoder.read(uint64)
|
||||
decoder.read(len.int, padRight)
|
||||
|
||||
func startTuple(decoder: var AbiDecoder, dynamic: bool) =
|
||||
func startTuple(decoder: var AbiDecoder, dynamic: bool): ?!void =
|
||||
var start: int
|
||||
if decoder.currentTuple.dynamic and dynamic:
|
||||
start = decoder.readOffset()
|
||||
start = ?decoder.readOffset()
|
||||
else:
|
||||
start = decoder.index
|
||||
decoder.stack.add(Tuple.init(start, dynamic))
|
||||
success()
|
||||
|
||||
func finishTuple(decoder: var AbiDecoder) =
|
||||
doAssert decoder.stack.len > 1, "unable to finish a tuple that hasn't started"
|
||||
|
@ -97,14 +103,16 @@ func finishTuple(decoder: var AbiDecoder) =
|
|||
if not tupl.dynamic:
|
||||
decoder.index = tupl.index
|
||||
|
||||
func decode(decoder: var AbiDecoder, T: type tuple): T =
|
||||
const dynamic = AbiEncoder.isDynamic(typeof(result))
|
||||
decoder.startTuple(dynamic)
|
||||
for element in result.fields:
|
||||
element = decoder.read(typeof(element))
|
||||
func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
|
||||
const dynamic = AbiEncoder.isDynamic(T)
|
||||
var tupl: T
|
||||
?decoder.startTuple(dynamic)
|
||||
for element in tupl.fields:
|
||||
element = ?decoder.read(typeof(element))
|
||||
decoder.finishTuple()
|
||||
success tupl
|
||||
|
||||
func read*(decoder: var AbiDecoder, T: type): T =
|
||||
func read*(decoder: var AbiDecoder, T: type): ?!T =
|
||||
decoder.decode(T)
|
||||
|
||||
func finish(decoder: var AbiDecoder) =
|
||||
|
@ -112,24 +120,29 @@ func finish(decoder: var AbiDecoder) =
|
|||
doAssert decoder.last == decoder.bytes.len, "unread trailing bytes found"
|
||||
doAssert decoder.last mod 32 == 0, "encoding variant broken"
|
||||
|
||||
func decode[T](decoder: var AbiDecoder, _: type seq[T]): seq[T] =
|
||||
let len = decoder.read(uint64)
|
||||
decoder.startTuple(dynamic=true)
|
||||
func decode[T](decoder: var AbiDecoder, _: type seq[T]): ?!seq[T] =
|
||||
var sequence: seq[T]
|
||||
let len = ?decoder.read(uint64)
|
||||
?decoder.startTuple(dynamic=true)
|
||||
for _ in 0..<len:
|
||||
result.add(decoder.read(T))
|
||||
sequence.add(?decoder.read(T))
|
||||
decoder.finishTuple()
|
||||
success sequence
|
||||
|
||||
func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): array[I,T] =
|
||||
func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): ?!array[I,T] =
|
||||
var arr: array[I, T]
|
||||
const dynamic = AbiEncoder.isDynamic(T)
|
||||
decoder.startTuple(dynamic)
|
||||
for i in 0..<result.len:
|
||||
result[i] = decoder.read(T)
|
||||
?decoder.startTuple(dynamic)
|
||||
for i in 0..<arr.len:
|
||||
arr[i] = ?decoder.read(T)
|
||||
decoder.finishTuple()
|
||||
success arr
|
||||
|
||||
func decode(decoder: var AbiDecoder, T: type string): T =
|
||||
string.fromBytes(decoder.read(seq[byte]))
|
||||
func decode(decoder: var AbiDecoder, T: type string): ?!T =
|
||||
success string.fromBytes(?decoder.read(seq[byte]))
|
||||
|
||||
func decode*(_: type AbiDecoder, bytes: seq[byte], T: type): T =
|
||||
func decode*(_: type AbiDecoder, bytes: seq[byte], T: type): ?!T =
|
||||
var decoder = AbiDecoder.init(bytes)
|
||||
result = decoder.decode(T)
|
||||
var value = ?decoder.decode(T)
|
||||
decoder.finish()
|
||||
success value
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import std/unittest
|
||||
import pkg/questionable/results
|
||||
import contractabi
|
||||
import ./examples
|
||||
|
||||
|
@ -6,7 +7,7 @@ suite "ABI decoding":
|
|||
|
||||
proc checkDecode[T](value: T) =
|
||||
let encoded = AbiEncoder.encode(value)
|
||||
check AbiDecoder.decode(encoded, T) == value
|
||||
check !AbiDecoder.decode(encoded, T) == value
|
||||
|
||||
proc checkDecode(T: type) =
|
||||
checkDecode(T.example)
|
||||
|
|
Loading…
Reference in New Issue