Library for turning Elm values into Protobuf messages.
The examples show
Bytes
values like this:<3A* 05* 68 65 6C 6C 6F>
. The*
means the byte is Protobuf metadata. It does not contain any real value. Here3A
means the next encoded field is a length delimited value for field number7
.05
is the number of bytes that was used to encode the value that follows. Those five bytes contain the stringhello
. Read this if you want to learn more about how Protobuf encoding works.
encode : Encoder -> Bytes
Turn an Encoder
into Bytes
.
encode (int32 127) -- <7F>
encode (sint32 127) -- <FE 01>
encode (sfixed32 127) -- <7F 00 00 00>
Values are encoded together with a field number and the
wire type
conform the specification in a .proto
file. This allows decoders to know what
field it is decoding and to read the right number of Bytes
.
import Protobuf.Encode as Encode
type alias Person =
{ age : Int
, name : String
}
toPersonEncoder : Person -> Encode.Encoder
toPersonEncoder person =
Encode.message
[ ( 1, Encode.uint32 person.age )
, ( 2, Encode.string person.name )
]
Encode.encode (encodePerson (Person 33 "Tom")) -- <08* 21 12* 03* 54 6F 6D>
You probably want to send these Bytes
in the body of an HTTP request:
import Http
import Protobuf.Encode as Encode
postPerson : (Result Http.Error () -> msg) -> Person -> Cmd msg
postPerson toMsg person =
Http.post
{ url = "https://example.com/person"
, body =
Http.bytesBody "application/octet-stream" <|
Encode.encode (encodePerson person)
, expect = Http.expectWhatever
}
Describes how to generate a sequence of bytes according to the specification of Protobuf.
message : List ( Basics.Int, Encoder ) -> Encoder
Encode a record into a message. For this you need to provide a list of
unique field numbers (between 1
and 536870911
) and their corresponding
Encoder
s.
type alias Foo =
{ a : Float
, b : String
, c : List Int
}
foo : Foo
foo =
Foo 1.25 "hello" [ 1, 2, 3, 4, 5 ]
toEncoder : Encoder
toEncoder =
message
[ ( 1, double foo.a ) -- <09* 00 00 00 00 00 00 F4 3F
, ( 2, string foo.b ) -- 12* 05* 68 65 6C 6C 6F
, ( 3, repeated int32 foo.c ) -- 1A* 05* 01 02 03 04 05>
]
int32 : Basics.Int -> Encoder
Encode integers from -2147483648
to 2147483647
into a message. Uses
variable-length encoding. Inefficient for encoding negative numbers – if your
field is likely to have negative values, use sint32
instead.
encode (int32 0) -- <00>
encode (int32 100) -- <64>
encode (int32 -100) -- <FF FF FF FF FF FF FF 9C>
This function can also be used to encode custom types as enumeration:
type Fruit
= Apple
| Banana
| Mango
| Unrecognized Int
toFruitEncoder : Fruit -> Encoder
toFruitEncoder value =
Encode.int32 <|
case value of
Apple ->
0
Banana ->
1
Mango ->
2
Unrecognized v ->
v
Note that for proto2
the Unrecognized Int
field can be left out.
uint32 : Basics.Int -> Encoder
Encode integers from 0
to 4294967295
into a message.
Uses variable-length encoding.
encode (uint32 0) -- <00>
encode (uint32 100) -- <64>
sint32 : Basics.Int -> Encoder
Encode integers from -2147483648
to 2147483647
into a message. Uses
variable-length encoding. This encoder encodes negative numbers more
efficiently than int32
.
encode (sint32 0) -- <00>
encode (sint32 100) -- <C8 01>
encode (sint32 -100) -- <C7 01>
fixed32 : Basics.Int -> Encoder
Encode integers from 0
to 4294967295
into a message. Always four bytes.
More efficient than uint32
if values are often greater than
268435456
.
encode (fixed32 0) -- <00 00 00 00>
encode (fixed32 100) -- <64 00 00 00>
sfixed32 : Basics.Int -> Encoder
Encode integers from -2147483648
to 2147483647
into a message.
Always four bytes.
encode (sfixed32 0) -- <00 00 00 00>
encode (sfixed32 100) -- <64 00 00 00>
encode (sfixed32 -100) -- <9C FF FF FF>
int64 : Protobuf.Types.Int64.Int64 -> Encoder
Encode integers from -9223372036854775808
to 9223372036854775807
into a message. Uses
variable-length encoding. Inefficient for encoding negative numbers – if your
field is likely to have negative values, use sint64
instead.
uint64 : Protobuf.Types.Int64.Int64 -> Encoder
Encode integers from 0
to 18446744073709551615
into a message.
Uses variable-length encoding.
sint64 : Protobuf.Types.Int64.Int64 -> Encoder
Encode integers from -9223372036854775808
to 9223372036854775807
into a message. Uses
variable-length encoding. This encoder encodes negative numbers more
efficiently than int64
.
fixed64 : Protobuf.Types.Int64.Int64 -> Encoder
Encode integers from 0
to 18446744073709551615
into a message.
Always eight bytes.
sfixed64 : Protobuf.Types.Int64.Int64 -> Encoder
Encode integers from -9223372036854775808
to 9223372036854775807
into a message.
Always eight bytes.
double : Basics.Float -> Encoder
Encode 64-bit floating point numbers into a message.
encode (double 0) -- <00 00 00 00 00 00 00 00>
encode (double 100) -- <00 00 00 00 00 00 59 40>
encode (double -100) -- <00 00 00 00 00 00 59 C0>
float : Basics.Float -> Encoder
Encode 32-bit floating point numbers into a message. The value may lose some precision by encoding it as a float.
encode (float 0) -- <00 00 00 00>
encode (float 100) -- <00 00 C8 42>
encode (float -100) -- <00 00 C8 C2>
string : String -> Encoder
Encode strings into a message.
encode (string "$20") -- <24 32 30>
encode (string "£20") -- <C2 A3 32 30>
encode (string "€20") -- <E2 82 AC 32 30>
encode (string "bread") -- <62 72 65 61 64>
encode (string "brød") -- <62 72 C3 B8 64>
bool : Basics.Bool -> Encoder
Encode booleans into a message.
encode (bool False) -- <00>
encode (bool True) -- <01>
bytes : Bytes -> Encoder
Copy raw Bytes
into a message.
-- bs == <0A 0B 0C>
encode (bytes bs) -- <0A 0B 0C>
none : Encoder
Encode nothing. Note that you can easily combine this encoder with any
field number to pass to message
as literally nothing will be
encoded.
This can be useful when encoding embedded messages:
type alias Report =
{ title : String
, contents : String
, attachment : Maybe Attachment
}
toReportEncoder : Report -> Encoder
toReportEncoder report =
message
[ ( 1, string report.title )
, ( 2, string report.contents )
, ( 3, Maybe.withDefault none <| Maybe.map toAttachmentEncoder report.attachment )
]
Or when encoding custom types:
type alias FormValue =
{ key : String
, value : Maybe Value
}
type Value
= StringValue String
| IntValue Int
toKeyValueEncoder : FormValue -> Encoder
toKeyValueEncoder formValue =
message
[ ( 1, string formValue.key )
, Maybe.withDefault ( 0, none ) <| Maybe.map toValueEncoder formValue.value
]
toValueEncoder : Value -> ( Int, Encoder )
toValueEncoder model =
case model of
StringValue value ->
( 2, string value )
IntValue value ->
( 3, int32 value )
list : (a -> Encoder) -> List a -> Encoder
Encode a list of values into a message. Protobuf support two kind of encodings:
-- packed encoding
message
[ ( 1, list int32 [ 1, 2, 3 ] ) -- <0A* 03* 01 02 03>
]
-- non-packed encoding
message
[ ( 1
, list string
[ "one" -- <0A* 03* 6F 6E 65
, "two" -- 0A* 03* 74 77 6F
, "three" -- 0A* 05* 74 68 72 65 65>
]
)
]
Packed encoding is preferred as it uses less bytes on the wire. list
will
automatically fall-back to non-packed encoding for non-scalar numeric types.
dict : (k -> Encoder) -> (v -> Encoder) -> Dict k v -> Encoder
Encode a dictionary of key-value pairs. This requires providing one encoder for the keys and one for the values.
let
value =
Dict.fromList
[ ( 1, "foo" ) -- <0A* 07* 08* 01 12* 03* 66 6F 6F
, ( 2, "bar" ) -- 0A* 07* 08* 02 12* 03* 62 61 72>
]
in
message [ ( 1, dict int32 string value ) ]