Parse Bytes with custom error reporting and context tracking.
Run the given decoder on the provided bytes.
import Bytes.Encode as E
import Bytes.Decode.Branchable as D
E.string "hello"
|> E.encode
|> D.decode (D.string 5)
--> Just "hello"
E.string "hello"
|> E.encode
|> D.decode (D.string 6)
--> NothingAlways succeed with the given value.
import Bytes.Encode as E
import Bytes.Decode.Branchable as D
E.encode (E.sequence [])
|> D.decode (D.succeed "hi there")
--> Just "hi there"A Decoder that always fails.
import Bytes.Encode as E
import Bytes.Decode.Branchable as D
E.sequence []
|> E.encode
|> D.decode D.fail
--> NothingDecode one byte into an integer from 0 to 255.
Decode two bytes into an integer from 0 to 65535.
Decode four bytes into an integer from 0 to 4294967295.
Decode one byte into an integer from -128 to 127.
Decode two bytes into an integer from -32768 to 32767.
Decode four bytes into an integer from -2147483648 to 2147483647.
Decode count bytes representing UTF-8 characters into a String.
Note that Elm strings use UTF-16. As a result, the String.length will not
always agree with the number of bytes that went into it!
import Bytes.Encode as E
import Bytes.Decode.Branchable as D
[ 0xF0, 0x9F, 0x91, 0x8D ]
|> List.map E.unsignedInt8
|> E.sequence
|> E.encode
|> D.decode (D.string 4)
--> Just "👍"Transform the value a decoder produces
import Bytes.Encode as E
import Bytes.Decode.Branchable as D
E.string "hello"
|> E.encode
|> D.decode (D.map String.length (D.string 5))
--> Just 5Combine two decoders into a single one.
import Bytes exposing (Bytes)
import Bytes.Encode as E
import Bytes.Decode.Branchable as D exposing (Decoder)
input : Bytes
input =
[ E.unsignedInt8 2
, E.string "wat"
]
|> E.sequence
|> E.encode
map2Example : Decoder String
map2Example =
D.map2 String.repeat D.unsignedInt8 (D.string 3)
D.decode map2Example input
--> Just "watwat"Note that the effect of map2 (and, in fact, every map variation) can also be
achieved using a combination of succeed and keep.
equivalent : Decoder String
equivalent =
D.succeed String.repeat
|> D.keep D.unsignedInt8
|> D.keep (D.string 3)
D.decode equivalent input
--> Just "watwat"Combine 3 decoders into a single one.
Combine 4 decoders into a single one.
Combine 5 decoders into a single one.
Keep the value produced by a decoder in a pipeline.
Together with succeed and ignore, this allows writing
pretty flexible decoders in a straightforward manner: the order in which things
are parsed is apparent.
import Bytes.Encode as E
import Bytes.Decode.Branchable as D exposing (Decoder)
decoder : Decoder (Int, Int)
decoder =
D.succeed Tuple.pair
|> D.keep D.unsignedInt8
|> D.ignore D.unsignedInt8
|> D.keep D.unsignedInt8
[ E.unsignedInt8 12
, E.unsignedInt8 3
, E.unsignedInt8 45
]
|> E.sequence
|> E.encode
|> D.decode decoder
--> Just ( 12, 45 )Ignore the value produced by a decoder.
Note that the decoder must still succeed for the pipeline to succeed. This means you can use this for checking the value of something, without using the value.
import Bytes.Encode as E
import Bytes.Decode.Branchable as D exposing (Decoder)
match : Int -> Decoder Int
match expected =
D.unsignedInt8
|> D.andThen
(\actual ->
if expected == actual then
D.succeed actual
else
D.fail
)
decoder : Decoder ()
decoder =
D.succeed ()
|> D.ignore (match 66)
E.unsignedInt8 66
|> E.encode
|> D.decode decoder
--> Just ()
E.unsignedInt8 44
|> E.encode
|> D.decode decoder
--> NothingSkip a number of bytes in a pipeline.
This is similar to ignore, but rather than decoding a value and discarding it,
this just goes ahead and skips them altogether.
Decode one thing, and then another thing based on the first thing.
This is very useful to make the content of your data drive your decoder. As an example, consider a string encoded as the length of the string, followed by the actual data:
import Bytes.Encode as E
import Bytes.Decode.Branchable as D exposing (Decoder)
string : Decoder String
string =
D.unsignedInt8
|> D.andThen D.string
[ E.unsignedInt8 5
, E.string "hello"
]
|> E.sequence
|> E.encode
|> D.decode string
--> Just "hello"Tries a bunch of decoders and succeeds with the first one to succeed.
All decoder alternatives start at the same location in the bytes.
Repeat a given decoder count times.
The order of arguments is based on the common occurence of reading the number of times to repeat something through a decoder.
import Bytes.Encode as E
import Bytes.Decode.Branchable as D exposing (Decoder)
intList : Decoder (List Int)
intList =
D.unsignedInt8
|> D.andThen (D.repeat D.unsignedInt8)
[ 5, 0, 1, 2, 3, 4 ]
|> List.map E.unsignedInt8
|> E.sequence
|> E.encode
|> D.decode intList
--> Just [ 0, 1, 2, 3, 4 ]Loop a decoder until it declares it is done looping.
The Step type in the signature comes from the Bytes.Decode module in elm/bytes.
Here is how repeat is defined for example.
import Bytes.Decode
import Bytes.Decode.Branchable as D exposing (Decoder)
repeat : Decoder value -> Int -> Decoder (List value)
repeat p count =
D.loop ( count, [] ) (repeatHelp p)
repeatHelp p ( count, acc ) =
if count <= 0 then
succeed (Bytes.Decode.Done (List.reverse acc))
else
map (\v -> Bytes.Decode.Loop ( count - 1, v :: acc )) p
A decoder for a certain type of value.