mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
test_gen: replace stdlib Json with inline implementation
Towards the goal of removing Json from stdlib, this change replaces usage of TotallyNotJson in test_gen/gen_abilities with a simple usable inline implementation of Encoder/DecoderFormatting. Similarly, the use of TotallyNotJson in test_reporting is not necessary at all and is replaced with a Decoder that wouldn't actually work, but which does compile.
This commit is contained in:
parent
05ab018380
commit
0faa1d5f20
8 changed files with 679 additions and 278 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3846,6 +3846,7 @@ dependencies = [
|
||||||
"roc_solve",
|
"roc_solve",
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
|
"roc_test_utils",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"roc_wasm_interp",
|
"roc_wasm_interp",
|
||||||
|
|
|
@ -11401,10 +11401,50 @@ In roc, functions are always written as a lambda, like{}
|
||||||
r#"
|
r#"
|
||||||
app "test" imports [] provides [main] to "./platform"
|
app "test" imports [] provides [main] to "./platform"
|
||||||
|
|
||||||
import TotallyNotJson
|
ErrDecoder := {} implements [DecoderFormatting {
|
||||||
|
u8: decodeU8,
|
||||||
|
u16: decodeU16,
|
||||||
|
u32: decodeU32,
|
||||||
|
u64: decodeU64,
|
||||||
|
u128: decodeU128,
|
||||||
|
i8: decodeI8,
|
||||||
|
i16: decodeI16,
|
||||||
|
i32: decodeI32,
|
||||||
|
i64: decodeI64,
|
||||||
|
i128: decodeI128,
|
||||||
|
f32: decodeF32,
|
||||||
|
f64: decodeF64,
|
||||||
|
dec: decodeDec,
|
||||||
|
bool: decodeBool,
|
||||||
|
string: decodeString,
|
||||||
|
list: decodeList,
|
||||||
|
record: decodeRecord,
|
||||||
|
tuple: decodeTuple,
|
||||||
|
}]
|
||||||
|
decodeU8 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeU16 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeU32 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeU64 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeU128 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeI8 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeI16 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeI32 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeI64 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeI128 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeF32 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeF64 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeDec = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeBool = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeString = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeList : Decoder elem (ErrDecoder) -> Decoder (List elem) (ErrDecoder)
|
||||||
|
decodeList = \_ -> Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeRecord : state, (state, Str -> [Keep (Decoder state (ErrDecoder)), Skip]), (state, (ErrDecoder) -> Result val DecodeError) -> Decoder val (ErrDecoder)
|
||||||
|
decodeRecord =\_, _, _ -> Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
decodeTuple : state, (state, U64 -> [Next (Decoder state (ErrDecoder)), TooLong]), (state -> Result val DecodeError) -> Decoder val (ErrDecoder)
|
||||||
|
decodeTuple = \_, _, _ -> Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
|
||||||
|
|
||||||
main =
|
main =
|
||||||
decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes TotallyNotJson.json
|
decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes (@ErrDecoder {})
|
||||||
when decoded is
|
when decoded is
|
||||||
Ok rcd -> rcd.first rcd.second
|
Ok rcd -> rcd.first rcd.second
|
||||||
_ -> "something went wrong"
|
_ -> "something went wrong"
|
||||||
|
@ -11415,7 +11455,7 @@ In roc, functions are always written as a lambda, like{}
|
||||||
|
|
||||||
This expression has a type that does not implement the abilities it's expected to:
|
This expression has a type that does not implement the abilities it's expected to:
|
||||||
|
|
||||||
8│ Ok rcd -> rcd.first rcd.second
|
48│ Ok rcd -> rcd.first rcd.second
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
||||||
I can't generate an implementation of the `Decoding` ability for
|
I can't generate an implementation of the `Decoding` ability for
|
||||||
|
|
|
@ -45,6 +45,7 @@ roc_reporting = { path = "../../reporting" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
roc_target = { path = "../roc_target" }
|
roc_target = { path = "../roc_target" }
|
||||||
|
roc_test_utils = { path = "../../test_utils"}
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_wasm_interp = { path = "../../wasm_interp" }
|
roc_wasm_interp = { path = "../../wasm_interp" }
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,18 +0,0 @@
|
||||||
app "test"
|
|
||||||
imports [TotallyNotJson]
|
|
||||||
provides [main] to "./platform"
|
|
||||||
|
|
||||||
HelloWorld := {} implements [Encoding {toEncoder}]
|
|
||||||
|
|
||||||
toEncoder = \@HelloWorld {} ->
|
|
||||||
Encode.custom \bytes, fmt ->
|
|
||||||
bytes
|
|
||||||
|> Encode.appendWith (Encode.string "Hello, World!\n") fmt
|
|
||||||
|
|
||||||
f =
|
|
||||||
when Str.fromUtf8 (Encode.toBytes (@HelloWorld {}) TotallyNotJson.json) is
|
|
||||||
Ok s -> s
|
|
||||||
_ -> "<bad>"
|
|
||||||
|
|
||||||
main = f
|
|
||||||
# ^ Str
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
app "test"
|
||||||
|
imports []
|
||||||
|
provides [main] to "./platform"
|
||||||
|
|
||||||
|
OnlyStrEncoder := {} implements [Encode.EncoderFormatting {
|
||||||
|
u8: encodeU8,
|
||||||
|
u16: encodeU16,
|
||||||
|
u32: encodeU32,
|
||||||
|
u64: encodeU64,
|
||||||
|
u128: encodeU128,
|
||||||
|
i8: encodeI8,
|
||||||
|
i16: encodeI16,
|
||||||
|
i32: encodeI32,
|
||||||
|
i64: encodeI64,
|
||||||
|
i128: encodeI128,
|
||||||
|
f32: encodeF32,
|
||||||
|
f64: encodeF64,
|
||||||
|
dec: encodeDec,
|
||||||
|
bool: encodeBool,
|
||||||
|
string: encodeString,
|
||||||
|
list: encodeList,
|
||||||
|
record: encodeRecord,
|
||||||
|
tuple: encodeTuple,
|
||||||
|
tag: encodeTag,
|
||||||
|
}]
|
||||||
|
|
||||||
|
encodeNothing = Encode.custom \bytes, @OnlyStrEncoder {} -> bytes
|
||||||
|
encodeU8 = \_n -> encodeNothing
|
||||||
|
encodeU16 = \_n -> encodeNothing
|
||||||
|
encodeU32 = \_n -> encodeNothing
|
||||||
|
encodeU64 = \_n -> encodeNothing
|
||||||
|
encodeU128 = \_n -> encodeNothing
|
||||||
|
encodeI8 = \_n -> encodeNothing
|
||||||
|
encodeI16 = \_n -> encodeNothing
|
||||||
|
encodeI32 = \_n -> encodeNothing
|
||||||
|
encodeI64 = \_n -> encodeNothing
|
||||||
|
encodeI128 = \_n -> encodeNothing
|
||||||
|
encodeF32 = \_n -> encodeNothing
|
||||||
|
encodeF64 = \_n -> encodeNothing
|
||||||
|
encodeDec = \_n -> encodeNothing
|
||||||
|
encodeBool = \_b -> encodeNothing
|
||||||
|
encodeString = \str -> Encode.custom \bytes, @OnlyStrEncoder {} -> List.concat bytes (Str.toUtf8 str)
|
||||||
|
encodeList : List elem, (elem -> Encoder OnlyStrEncoder) -> Encoder OnlyStrEncoder
|
||||||
|
encodeList = \_lst, _encodeElem -> encodeNothing
|
||||||
|
encodeRecord : List {key: Str, value: Encoder OnlyStrEncoder} -> Encoder OnlyStrEncoder
|
||||||
|
encodeRecord = \_fields -> encodeNothing
|
||||||
|
encodeTuple : List (Encoder OnlyStrEncoder) -> Encoder OnlyStrEncoder
|
||||||
|
encodeTuple = \_elems -> encodeNothing
|
||||||
|
encodeTag : Str, List (Encoder OnlyStrEncoder) -> Encoder OnlyStrEncoder
|
||||||
|
encodeTag = \_name, _payload -> encodeNothing
|
||||||
|
|
||||||
|
|
||||||
|
HelloWorld := {} implements [Encoding {toEncoder}]
|
||||||
|
|
||||||
|
toEncoder = \@HelloWorld {} ->
|
||||||
|
Encode.custom \bytes, fmt ->
|
||||||
|
bytes
|
||||||
|
|> Encode.appendWith (Encode.string "Hello, World!\n") fmt
|
||||||
|
|
||||||
|
f =
|
||||||
|
when Str.fromUtf8 (Encode.toBytes (@HelloWorld {}) (@OnlyStrEncoder {})) is
|
||||||
|
Ok s -> s
|
||||||
|
_ -> "<bad>"
|
||||||
|
|
||||||
|
main = f
|
||||||
|
# ^ Str
|
261
crates/test_utils/src/TagLenEncoderFmt.roc
Normal file
261
crates/test_utils/src/TagLenEncoderFmt.roc
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
# a simple encoder/decoder format
|
||||||
|
#
|
||||||
|
# HACK: since this file is inlined into test code, it can't be a proper module
|
||||||
|
#
|
||||||
|
# the original author found it useful to leave this header here as a comment, to
|
||||||
|
# make it easy to switch back and forth between editing it as a proper roc module
|
||||||
|
# (so things like language server and `roc test` work), and inlining it into test
|
||||||
|
# code.
|
||||||
|
#
|
||||||
|
# module [
|
||||||
|
# TagLenFmt,
|
||||||
|
# tagLenFmt,
|
||||||
|
# ]
|
||||||
|
|
||||||
|
TagLenFmt := {}
|
||||||
|
implements [
|
||||||
|
EncoderFormatting {
|
||||||
|
u8: encodeU8,
|
||||||
|
u16: encodeU16,
|
||||||
|
u32: encodeU32,
|
||||||
|
u64: encodeU64,
|
||||||
|
u128: encodeU128,
|
||||||
|
i8: encodeI8,
|
||||||
|
i16: encodeI16,
|
||||||
|
i32: encodeI32,
|
||||||
|
i64: encodeI64,
|
||||||
|
i128: encodeI128,
|
||||||
|
f32: encodeF32,
|
||||||
|
f64: encodeF64,
|
||||||
|
dec: encodeDec,
|
||||||
|
bool: encodeBool,
|
||||||
|
string: encodeString,
|
||||||
|
list: encodeList,
|
||||||
|
record: encodeRecord,
|
||||||
|
tuple: encodeTuple,
|
||||||
|
tag: encodeTag,
|
||||||
|
},
|
||||||
|
DecoderFormatting {
|
||||||
|
u8: decodeU8,
|
||||||
|
u16: decodeU16,
|
||||||
|
u32: decodeU32,
|
||||||
|
u64: decodeU64,
|
||||||
|
u128: decodeU128,
|
||||||
|
i8: decodeI8,
|
||||||
|
i16: decodeI16,
|
||||||
|
i32: decodeI32,
|
||||||
|
i64: decodeI64,
|
||||||
|
i128: decodeI128,
|
||||||
|
f32: decodeF32,
|
||||||
|
f64: decodeF64,
|
||||||
|
dec: decodeDec,
|
||||||
|
bool: decodeBool,
|
||||||
|
string: decodeString,
|
||||||
|
list: decodeList,
|
||||||
|
record: decodeRecord,
|
||||||
|
tuple: decodeTuple,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
tagLenFmt = @TagLenFmt {}
|
||||||
|
|
||||||
|
# ENCODE
|
||||||
|
|
||||||
|
appendPreLen = \bytes, pre, len ->
|
||||||
|
List.append bytes (Num.toU8 pre)
|
||||||
|
|> List.concat (Num.toStr len |> Str.toUtf8)
|
||||||
|
|> List.append ' '
|
||||||
|
encodeNum = \n -> Encode.custom \bytes, @TagLenFmt {} -> appendPreLen bytes 'n' n
|
||||||
|
|
||||||
|
encodeU8 = encodeNum
|
||||||
|
encodeU16 = encodeNum
|
||||||
|
encodeU32 = encodeNum
|
||||||
|
encodeU64 = encodeNum
|
||||||
|
encodeU128 = encodeNum
|
||||||
|
encodeI8 = encodeNum
|
||||||
|
encodeI16 = encodeNum
|
||||||
|
encodeI32 = encodeNum
|
||||||
|
encodeI64 = encodeNum
|
||||||
|
encodeI128 = encodeNum
|
||||||
|
encodeF32 = encodeNum
|
||||||
|
encodeF64 = encodeNum
|
||||||
|
encodeDec = encodeNum
|
||||||
|
encodeBool = \b -> encodeU8 (if b then 1 else 0)
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Encode.toBytes 1 tagLenFmt
|
||||||
|
actual == (Str.toUtf8 "n1 ")
|
||||||
|
expect
|
||||||
|
actual = Encode.toBytes 1.3dec tagLenFmt
|
||||||
|
actual == (Str.toUtf8 "n1.3 ")
|
||||||
|
expect
|
||||||
|
actual = Encode.toBytes Bool.true tagLenFmt
|
||||||
|
actual == (Str.toUtf8 "n1 ")
|
||||||
|
|
||||||
|
encodeString = \str -> Encode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
appendPreLen bytes 's' (Str.countUtf8Bytes str)
|
||||||
|
|> List.concat (Str.toUtf8 str)
|
||||||
|
|> List.append ' '
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Encode.toBytes "hey" tagLenFmt
|
||||||
|
actual == (Str.toUtf8 "s3 hey ")
|
||||||
|
|
||||||
|
encodeList = \lst, encodeElem -> Encode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
bytesPre = appendPreLen bytes 'l' (List.len lst)
|
||||||
|
List.walk lst bytesPre \buf, elem ->
|
||||||
|
Encode.appendWith buf (encodeElem elem) (@TagLenFmt {})
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Encode.toBytes [1, 2, 3] tagLenFmt
|
||||||
|
actual == (Str.toUtf8 "l3 n1 n2 n3 ")
|
||||||
|
|
||||||
|
encodeRecord = \fields -> Encode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
bytesPre =
|
||||||
|
appendPreLen bytes 'r' (List.len fields)
|
||||||
|
List.walk fields bytesPre \buf, { key, value } ->
|
||||||
|
Encode.appendWith buf (encodeString key) (@TagLenFmt {})
|
||||||
|
|> Encode.appendWith value (@TagLenFmt {})
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Encode.toBytes { foo: "foo", bar: Bool.true } tagLenFmt
|
||||||
|
actual == Str.toUtf8 "r2 s3 bar n1 s3 foo s3 foo "
|
||||||
|
|
||||||
|
encodeTuple = \elems -> encodeList elems (\e -> e)
|
||||||
|
encodeTag = \name, payload -> encodeTuple (List.prepend payload (encodeString name))
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Encode.toBytes (1, "foo", {}) tagLenFmt
|
||||||
|
actual == (Str.toUtf8 "l3 n1 s3 foo r0 ")
|
||||||
|
|
||||||
|
# DECODE
|
||||||
|
|
||||||
|
splitAtSpace = \bytes ->
|
||||||
|
when List.splitFirst bytes ' ' is
|
||||||
|
Ok { before, after } -> { taken: before, rest: after }
|
||||||
|
Err _ -> { taken: [], rest: bytes }
|
||||||
|
|
||||||
|
decodeNumPre = \bytes, pre, toNum ->
|
||||||
|
when List.split bytes 1 is
|
||||||
|
{ before: [b], others } if b == pre ->
|
||||||
|
{ taken, rest } = splitAtSpace others
|
||||||
|
str = taken |> Str.fromUtf8 |> Result.mapErr \_ -> TooShort
|
||||||
|
result = Result.try str \s -> (toNum s |> Result.mapErr \_ -> TooShort)
|
||||||
|
when result is
|
||||||
|
Ok _ -> { result, rest }
|
||||||
|
Err _ -> { result, rest: others }
|
||||||
|
|
||||||
|
_ -> { result: Err TooShort, rest: bytes }
|
||||||
|
|
||||||
|
decodeNum = \toNum -> Decode.custom \bytes, @TagLenFmt {} -> decodeNumPre bytes 'n' toNum
|
||||||
|
|
||||||
|
decodeU8 = decodeNum Str.toU8
|
||||||
|
decodeU16 = decodeNum Str.toU16
|
||||||
|
decodeU32 = decodeNum Str.toU32
|
||||||
|
decodeU64 = decodeNum Str.toU64
|
||||||
|
decodeU128 = decodeNum Str.toU128
|
||||||
|
decodeI8 = decodeNum Str.toI8
|
||||||
|
decodeI16 = decodeNum Str.toI16
|
||||||
|
decodeI32 = decodeNum Str.toI32
|
||||||
|
decodeI64 = decodeNum Str.toI64
|
||||||
|
decodeI128 = decodeNum Str.toI128
|
||||||
|
decodeF32 = decodeNum Str.toF32
|
||||||
|
decodeF64 = decodeNum Str.toF64
|
||||||
|
decodeDec = decodeNum Str.toDec
|
||||||
|
decodeBool = Decode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
{ result: numResult, rest } = Decode.decodeWith bytes decodeU8 (@TagLenFmt {})
|
||||||
|
when numResult is
|
||||||
|
Ok 1 -> { result: Ok Bool.true, rest }
|
||||||
|
Ok 0 -> { result: Ok Bool.false, rest }
|
||||||
|
_ -> { result: Err TooShort, rest: bytes }
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Decode.fromBytes (Str.toUtf8 "n1 ") tagLenFmt
|
||||||
|
actual == Ok (Num.toU8 1)
|
||||||
|
expect
|
||||||
|
actual = Decode.fromBytes (Str.toUtf8 "n1 ") tagLenFmt
|
||||||
|
actual == Ok Bool.true
|
||||||
|
|
||||||
|
decodeLenPre = \bytes, pre -> decodeNumPre bytes pre Str.toU64
|
||||||
|
|
||||||
|
decodeTry = \{ result, rest }, map ->
|
||||||
|
when result is
|
||||||
|
Ok a -> map a rest
|
||||||
|
Err e -> { result: Err e, rest }
|
||||||
|
|
||||||
|
decodeString = Decode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
decodeLenPre bytes 's'
|
||||||
|
|> decodeTry \len, lenRest ->
|
||||||
|
{ before, others } = List.split lenRest len
|
||||||
|
result = Str.fromUtf8 before |> Result.mapErr \_ -> TooShort
|
||||||
|
when List.split others 1 is
|
||||||
|
{ before: [' '], others: rest } -> { result, rest }
|
||||||
|
_ -> { result: Err TooShort, rest: others }
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Decode.fromBytes (Str.toUtf8 "s3 foo ") tagLenFmt
|
||||||
|
actual == Ok "foo"
|
||||||
|
|
||||||
|
repeatDecode : U8, List U8, state, (state -> Decode.Decoder state TagLenFmt) -> DecodeResult state
|
||||||
|
repeatDecode = \pre, bytes, state, stepState ->
|
||||||
|
run = \end, bs ->
|
||||||
|
List.range { start: At 0, end: Before end }
|
||||||
|
|> List.walk { result: Ok state, rest: bs } \res, _i ->
|
||||||
|
decodeTry res \s, rest ->
|
||||||
|
Decode.decodeWith rest (stepState s) (@TagLenFmt {})
|
||||||
|
|
||||||
|
decodeLenPre bytes pre |> decodeTry run
|
||||||
|
|
||||||
|
decodeList = \elemDecoder -> Decode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
step = \lst -> Decode.custom \sbytes, @TagLenFmt {} ->
|
||||||
|
Decode.decodeWith sbytes elemDecoder (@TagLenFmt {})
|
||||||
|
|> Decode.mapResult \elem -> List.append lst elem
|
||||||
|
repeatDecode 'l' bytes [] step
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Decode.fromBytes (Str.toUtf8 "l3 n1 n2 n3 ") tagLenFmt
|
||||||
|
actual == Ok [1, 2, 3]
|
||||||
|
|
||||||
|
decodeRecord = \initState, stepField, finalizer -> Decode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
flattenFieldRes = \next, rest ->
|
||||||
|
when next is
|
||||||
|
Keep valueDecoder -> { result: Ok valueDecoder, rest }
|
||||||
|
Skip -> { result: Err TooShort, rest }
|
||||||
|
|
||||||
|
step = \state -> Decode.custom \sbytes, @TagLenFmt {} ->
|
||||||
|
Decode.decodeWith sbytes decodeString (@TagLenFmt {})
|
||||||
|
|> decodeTry \key, bs ->
|
||||||
|
flattenFieldRes (stepField state key) bs
|
||||||
|
|> decodeTry \valueDecoder, bs ->
|
||||||
|
Decode.decodeWith bs valueDecoder (@TagLenFmt {})
|
||||||
|
|
||||||
|
repeatDecode 'r' bytes initState step
|
||||||
|
|> decodeTry \state, rest -> { result: finalizer state (@TagLenFmt {}), rest }
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Decode.fromBytes (Str.toUtf8 "r2 s3 bar n1 s3 foo s3 foo ") tagLenFmt
|
||||||
|
actual == Ok ({ foo: "foo", bar: Bool.true })
|
||||||
|
|
||||||
|
decodeTuple = \initialState, stepElem, finalizer -> Decode.custom \bytes, @TagLenFmt {} ->
|
||||||
|
flattenFieldRes = \next, rest ->
|
||||||
|
when next is
|
||||||
|
Next dec -> { result: Ok dec, rest }
|
||||||
|
TooLong -> { result: Err TooShort, rest }
|
||||||
|
step = \{ state, i } -> Decode.custom \sbytes, @TagLenFmt {} ->
|
||||||
|
flattenFieldRes (stepElem state i) sbytes
|
||||||
|
|> decodeTry \dec, rest -> Decode.decodeWith rest dec (@TagLenFmt {})
|
||||||
|
|> Decode.mapResult \s -> { state: s, i: i + 1 }
|
||||||
|
|
||||||
|
repeatDecode 'l' bytes { state: initialState, i: 0 } step
|
||||||
|
|> decodeTry \s, rest -> { result: finalizer s.state, rest }
|
||||||
|
|
||||||
|
expect
|
||||||
|
actual = Decode.fromBytes (Str.toUtf8 "l3 n1 s3 abc l1 n0 ") tagLenFmt
|
||||||
|
actual == Ok (1, "abc", [Bool.false])
|
||||||
|
|
||||||
|
expect
|
||||||
|
input = { foo: (1, "abc", [Bool.false, Bool.true]), bar: { baz: 0.32 } }
|
||||||
|
encoded = Encode.toBytes input tagLenFmt
|
||||||
|
decoded = Decode.fromBytes encoded tagLenFmt
|
||||||
|
decoded == Ok input
|
||||||
|
|
|
@ -18,3 +18,11 @@ macro_rules! assert_multiline_str_eq {
|
||||||
$crate::_pretty_assert_eq!($crate::DebugAsDisplay($a), $crate::DebugAsDisplay($b))
|
$crate::_pretty_assert_eq!($crate::DebugAsDisplay($a), $crate::DebugAsDisplay($b))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a very simple implementation of En/DecoderFormatting to be embedded in roc source under test
|
||||||
|
///
|
||||||
|
/// - numbers and bools are encoded as 'n' <num> ' '
|
||||||
|
/// - strings are encoded as 's' <count utf-8 bytes> ' ' <str> ' '
|
||||||
|
/// - records are encoded as 'r' <number of fields> ' ' [<key><value>]*
|
||||||
|
/// - lists and tuples are encoded as 'l' <len> ' ' [<elem>]*
|
||||||
|
pub const TAG_LEN_ENCODER_FMT: &str = include_str!("TagLenEncoderFmt.roc");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue