Library for turning Protobuf messages into Elm values.
Turn a sequence of bytes into a nice Elm value.
decode int32 <7F> -- Just 127
decode sint32 <7F> -- Just -64
decode sfixed32 <7F> -- NothingThe Decoder specifies exactly how this should happen. This process may fail
if:
proto2 only);The examples above show a case where there are not enough bytes. They also show
the same bytes sequence can lead to different values depending on the Decoder
that is being used. Decoders cannot always detect these kind of mismatches.
Values are always encoded together with a field number and their wire type . This allows the decoder to set the right fields and to process the correct number of bytes.
Turn a Decoder into a Http.Expect. You probably received
the Bytes you want to decode from an HTTP request. As message
consumes all remaining bytes on the wire, you cannot use Http.expectBytes
directly (as it is not aware of the width of the bytes sequence). Hence, you
might want to use the expectBytes as provided by this package.
import Http
import Protobuf.Decode as Decode
getPerson : (Result Http.Error a -> msg) -> Cmd msg
getPerson toMsg =
Http.get
{ url = "https://example.com/person"
, Decode.expectBytes toMsg personDecoder
}Describes how to decode a certain field in a Protobuf-encoded message and how to update a record with the new Elm value.
Decode all remaining bytes into an record. The initial value given here
holds all default values (which cannot be overridden for proto3). Each
provided field decoder calls a setter function to update the record when its
field number is encountered on the bytes sequence. Unknown fields that have
no matching field decoder are currently being ignored.
import Protobuf.Decode as Decode
type alias Person =
{ name : String
}
personDecoder : Decode.Decoder Person
personDecoder =
-- Person "John"
Decode.message (Person "John") []Decode a required field. Decoding a message fails when one of its required
fields is not present in the bytes sequence. Required fields are only supported
in proto2.
type alias Person =
{ age : Int -- field number 1
, name : String -- field number 3
}
personDecoder : Decode.Decoder Person
personDecoder =
-- <08 21 1A 04 4A 6F 68 6E> == Just (Person 33 "John")
-- <08 21> == Nothing
-- <> == Nothing
Decode.message (Person 0 "")
[ Decode.required 1 int32 setAge
, Decode.required 3 string setName
]
-- SETTERS
setAge : a -> { b | age : a } -> { b | age : a }
setAge value model =
{ model | age = value }
setName : a -> { b | name : a } -> { b | name : a }
setName value model =
{ model | name = value }Decode an optional field.
import Protobuf.Decode as Decode
type alias Person =
{ age : Int -- field number 2
, name : String -- field number 4
}
personDecoder : Decode.Decoder Person
personDecoder =
-- <08 21 1A 04 4A 6F 68 6E> == Just (Person 33 "John")
-- <08 21> == Just (Person 33 "")
-- <> == Just (Person 0 "")
Decode.message (Person 0 "")
[ Decode.optional 2 int32 setAge
, Decode.optional 4 string setName
]
-- SETTERS
setAge : a -> { b | age : a } -> { b | age : a }
setAge value model =
{ model | age = value }
setName : a -> { b | name : a } -> { b | name : a }
setName value model =
{ model | name = value }Decode a repeated field. If no such fields are present when decoding a message, the result will be an empty list.
As repeated fields may occur multiple times in a bytes sequence, repeated
also needs to get hold of the record's current value in order to append the new
value.
import Protobuf.Decode as Decode
type alias Person =
{ names : List String -- field number 5
}
personDecoder : Decode.Decoder Person
personDecoder =
-- <2A 04 4A 6F 68 6E 2A 07 4D 61 72 77 6F 6F 64> == Just (Person [ "John", "Marwood" ])
-- <2A 04 4A 6F 68 6E> == Just (Person [ "John" ])
-- <> == Just (Person [])
Decode.message (Person [])
[ Decode.repeated 5 string .names setNames
]
-- SETTERS
setNames : a -> { b | names : a } -> { b | names : a }
setNames value model =
{ model | names = value }Decode a map field. If no such fields are present when decoding a message,
the result will be an empty Dict. Note that you need to provide one decoder
for the keys and another one for the values. Keys without a value or values
without a key stick to the provided defaults.
As map fields may occur multiple times in a bytes sequence, mapped
also needs to get hold of the record's current value in order to append the new
value.
import Dict exposing (Dict)
import Protobuf.Decode as Decode
type alias Administration =
{ persons : Dict Int String -- field number 6
}
administrationDecoder : Decode.Decoder Administration
administrationDecoder =
-- <32 08 08 01 12 04 4A 6F 68 6E 32 08 08 02 12 04 4B 61 74 65> == Just (Administration (Dict.fromList [( 1, "John" ), ( 2, "Kate" )])
-- <32 08 08 01 12 04 4A 6F 68 6E> == Just (Administration (Dict.fromList [( 1, "John" )])
-- <32 08 08 01> == Just (Administration (Dict.fromList [( 1, "" )])
-- <> == Just (Administration Dict.empty)
Decode.message (Administration Dict.empty)
[ Decode.mapped 6 ( 0, "" ) int32 string .persons setPersons
]
-- SETTERS
setPersons : a -> { b | persons : a } -> { b | persons : a }
setPersons value model =
{ model | persons = value }Decode one of some fields. As the decoder is capable of deserializing different types of data its return type must be a custom type.
import Protobuf.Decode as Decode
type alias FormValue =
{ key : String -- field number 7
, value : Maybe Value -- field number 8 or 9
}
type Value
= StringValue String
| IntValue Int
formValueDecoder : Decode.Decoder FormValue
formValueDecoder =
-- <0A 03 6B 65 79 12 05 76 61 6C 75 65> == Just (FormValue "key" (StringValue "value"))
-- <0A 03 6B 65 79 10 64> == Just (FormValue "key" (IntValue 100))
-- <0A 03 6B 65 79> == Just (FormValue "key" NoValue)
-- <> == Just (FormValue "" NoValue)
Decode.message (FormValue "" NoValue)
[ Decode.optional 7 string setKey
, Decode.oneOf
[ ( 8, Decode.map StringValue Decode.string )
, ( 9, Decode.map IntValue Decode.int32 )
]
setValue
]
-- SETTERS
setKey : a -> { b | key : a } -> { b | key : a }
setKey value model =
{ model | key = value }
setValue : a -> { b | value : a } -> { b | value : a }
setValue value model =
{ model | value = value }Decode a variable number of bytes into an integer from -2147483648 to 2147483647.
Decode a variable number of bytes into an integer from -2147483648 to 2147483647.
Decode a variable number of bytes into an integer from -9223372036854775808 to 9223372036854775807.
Decode a variable number of bytes into an integer from 0 to 18446744073709551615.
Decode a variable number of bytes into an integer from -9223372036854775808 to 9223372036854775807.
Decode eight bytes into an integer from -9223372036854775808 to 9223372036854775807.
Transform the value produced by a decoder. This is useful when encoding custom types as an enumeration:
type Fruit
= Apple
| Banana
| Mango
| Unrecognized Int
fruitDecoder : Decoder Fruit
fruitDecoder =
Decode.int32
|> Decode.map
(\value ->
case value of
0 ->
Apple
1 ->
Banana
2 ->
Mango
v ->
Unrecognized v
)Unrecognized Int is only used for values that are present but not known. For
proto2 decoding it is left out and unrecognized values are left out.
Sometimes you have messages with a recursive structure, like nested
comments. You must use lazyto make sure your decoder unrolls lazily.
type alias Comment =
{ message : String
, responses : Responses
}
type Responses
= Responses (List Comment)
commentDecoder : Decoder Comment
commentDecoder =
Decode.message (Comment "" (Responses []))
[ Decode.optional 1 Decode.string setMessage
, Decode.repeated 2
(Decode.lazy (\_ -> commentDecoder))
(unwrapResponses << .responses)
(setResponses << Responses)
]
-- SETTERS
setMessage : a -> { b | message : a } -> { b | message : a }
setMessage value model =
{ model | message = value }
setResponses : a -> { b | responses : a } -> { b | responses : a }
setResponses value model =
{ model | responses = value }
unwrapResponses : Responses -> List Comment
unwrapResponses (Responses responses) =
responsesHere you can read more about recursive data structures.
Describes how to turn a sequence of Protobuf-encoded bytes into a nice Elm value.