| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Proto3.Suite.Class
Description
This module provides type classes for encoding and decoding protocol
buffers message, as well as a safer alternative to the raw Wire
library based on Generics.
Classes
The Primitive class captures those types which correspond to primitive field
types, as defined by the protocol buffers specification. A Primitive type is
one which can always be encoded as a single key/value pair in the wire format.
The MessageField class captures those types which are encoded under a single
key in the wire format, i.e. primitives, packed and unpacked lists, and
embedded messages.
The Message class captures types which correspond to protocol buffers messages.
Instances of Message can be written by hand for your types by using the
functions in the Encode and Decode
modules. In the case where the message format is determined by your Haskell code,
you might prefer to derive your Message instances using generic deriving.
Generic Instances
Using the Generics approach, instead of generating Haskell code from a
.proto file, we write our message formats as Haskell types, and generate a
serializer/deserializer pair.
To use this library, simply derive a Generic instance for your type(s), and
use the default Message instance.
For generic Message instances, field numbers are automatically generated,
starting at 1. Therefore, adding new fields is a compatible change only at the
end of a record. Renaming fields is also safe. You should not use the generic
instances if you are starting from an existing .proto file.
Strings
Use Text instead of String for string types inside messages.
Example
data MultipleFields =
MultipleFields { multiFieldDouble :: Double
, multiFieldFloat :: Float
, multiFieldInt32 :: Int32
, multiFieldInt64 :: Int64
, multiFieldString :: TL.Text
, multiFieldBool :: Bool
} deriving (Show, Generic, Eq)
instance Message MultipleFields
serialized = toLazyByteString $ MultipleFields 1.0 1.0 1 1 "hi" True
deserialized :: MultipleFields
deserialized = case parse (toStrict serialized) of
Left e -> error e
Right msg -> msgSynopsis
- class Primitive a where
- encodePrimitive :: FieldNumber -> a -> MessageBuilder
- decodePrimitive :: Parser RawPrimitive a
- primType :: Proxy# a -> DotProtoPrimType
- class MessageField a where
- encodeMessageField :: FieldNumber -> a -> MessageBuilder
- decodeMessageField :: Parser RawField a
- protoType :: Proxy# a -> DotProtoField
- class Message a where
- encodeMessage :: FieldNumber -> a -> MessageBuilder
- decodeMessage :: FieldNumber -> Parser RawMessage a
- dotProto :: Proxy# a -> [DotProtoField]
- toLazyByteString :: Message a => a -> ByteString
- class HasDefault a where
- fromByteString :: Message a => ByteString -> Either ParseError a
- fromB64 :: Message a => ByteString -> Either ParseError a
- coerceOver :: forall {k} (a :: k) (b :: k) f. Coercible (f a) (f b) => f a -> f b
- unsafeCoerceOver :: forall {k} (a :: k) (b :: k) f. f a -> f b
- class ZigZag a where
- type ZigZagEncoded a
- zigZagEncode :: Signed a -> ZigZagEncoded a
- zigZagDecode :: ZigZagEncoded a -> Signed a
- class Named a where
- class ProtoEnum a => Finite a where
- message :: (Message a, Named a) => Proxy# a -> DotProtoDefinition
- enum :: (Finite e, Named e) => Proxy# e -> DotProtoDefinition
- class GenericMessage (f :: Type -> Type) where
- type GenericFieldCount (f :: Type -> Type) :: Nat
- genericEncodeMessage :: FieldNumber -> f a -> MessageBuilder
- genericDecodeMessage :: FieldNumber -> Parser RawMessage (f a)
- genericDotProto :: Proxy# f -> [DotProtoField]
Documentation
class Primitive a where Source #
This class captures those types which correspond to primitives in the protocol buffers specification.
It should be possible to fully reconstruct values of these types from
a single RawPrimitive. Notably, then, Nested is not Primitive even
though it can be embedded, since a nested message may by split up over
multiple embedded fields.
Minimal complete definition
Methods
encodePrimitive :: FieldNumber -> a -> MessageBuilder Source #
Encode a primitive value
decodePrimitive :: Parser RawPrimitive a Source #
Decode a primitive value
primType :: Proxy# a -> DotProtoPrimType Source #
Get the type which represents this type inside another message.
Instances
class MessageField a where Source #
This class captures those types which can appear as message fields in
the protocol buffers specification, i.e. Primitive types, or lists of
Primitive types
Minimal complete definition
Nothing
Methods
encodeMessageField :: FieldNumber -> a -> MessageBuilder Source #
Encode a message field
default encodeMessageField :: (HasDefault a, Primitive a) => FieldNumber -> a -> MessageBuilder Source #
decodeMessageField :: Parser RawField a Source #
Decode a message field
default decodeMessageField :: (HasDefault a, Primitive a) => Parser RawField a Source #
protoType :: Proxy# a -> DotProtoField Source #
Get the type which represents this type inside another message.
Instances
class Message a where Source #
This class captures those types which correspond to protocol buffer messages.
Minimal complete definition
Nothing
Methods
encodeMessage :: FieldNumber -> a -> MessageBuilder Source #
Encode a message
default encodeMessage :: (Generic a, GenericMessage (Rep a)) => FieldNumber -> a -> MessageBuilder Source #
decodeMessage :: FieldNumber -> Parser RawMessage a Source #
Decode a message
default decodeMessage :: (Generic a, GenericMessage (Rep a)) => FieldNumber -> Parser RawMessage a Source #
dotProto :: Proxy# a -> [DotProtoField] Source #
Generate a .proto message from the type information.
default dotProto :: GenericMessage (Rep a) => Proxy# a -> [DotProtoField] Source #
Instances
Encoding
toLazyByteString :: Message a => a -> ByteString Source #
Serialize a message as a lazy ByteString.
Decoding
class HasDefault a where Source #
A class for types with default values per the protocol buffers spec.
Minimal complete definition
Nothing
Methods
The default value for this type.
Instances
fromByteString :: Message a => ByteString -> Either ParseError a Source #
Parse any message that can be decoded.
fromB64 :: Message a => ByteString -> Either ParseError a Source #
As fromByteString, except the input bytestring is base64-encoded.
coerceOver :: forall {k} (a :: k) (b :: k) f. Coercible (f a) (f b) => f a -> f b Source #
Like coerce but lets you avoid specifying a type constructor
(such a as parser) that is common to both the input and output types.
unsafeCoerceOver :: forall {k} (a :: k) (b :: k) f. f a -> f b Source #
Like unsafeCoerce but lets you avoid specifying a type constructor
(such a as parser) that is common to both the input and output types.
Minimal complete definition
Nothing
Associated Types
type ZigZagEncoded a Source #
The unsigned integral type used to hold the value after ZigZag encoding but before varint encoding, and after varint decoding but before ZigZag decoding.
NOTE: The two integral types must have the same width both to
correctly encode large sint32 values and, during decoding,
to compensate for overlong encodings emitted by versions of
this library before v0.8.2. Those older versions incorrectly
sign-extended ZigZag-encoded sint32 values in packed fields.
Methods
zigZagEncode :: Signed a -> ZigZagEncoded a Source #
Importantly, the resulting unsigned integer has the same width as the input type, so that any integral promotion before or during varint encoding will zero-pad instead of sign-extend.
Sign extension would result in a more bulky encoding
and would violate the compatibility guarantee in
https://protobuf.dev/programming-guides/proto3/#updating that
an sint32 value can be decoded as if it had type sint64.
default zigZagEncode :: (Num a, FiniteBits a, Integral a, Num (ZigZagEncoded a)) => Signed a -> ZigZagEncoded a Source #
zigZagDecode :: ZigZagEncoded a -> Signed a Source #
Importantly, the given unsigned integer has the same width as the result type. If the encoder was a version of this library before v0.8.2, and the field was packed, it would have incorrectly sign-extended between the ZigZag encoding step and the varint encoding step, rather than zero-padding. By narrowing before we ZigZag decode, we exclude the incorrect bits.
Maintaining compatibility with versions of this library before v0.8.2
does have a curious side effect. When an sint64 value outside the
range of sint32 is decoded as type sint32, the sign will be
preserved and the magnitude decreased, rather than the more typical
conversion that outputs the remainder after division by 2^32, as
would happen with 'fromIntegral Int64 Int32'. One could argue that
our behavior is surprising and unusual. However, both narrowings
lose information, and neither is supported by protobuf:
https://protobuf.dev/programming-guides/dos-donts/ says,
"However, changing a field's message type will break
unless the new message is a superset of the old one."
default zigZagDecode :: (Num (ZigZagEncoded a), Bits (ZigZagEncoded a), Integral (ZigZagEncoded a), Num a) => ZigZagEncoded a -> Signed a Source #
Instances
| ZigZag Int32 Source # | |||||
Defined in Proto3.Suite.Class Associated Types
Methods zigZagEncode :: Signed Int32 -> ZigZagEncoded Int32 Source # zigZagDecode :: ZigZagEncoded Int32 -> Signed Int32 Source # | |||||
| ZigZag Int64 Source # | |||||
Defined in Proto3.Suite.Class Associated Types
Methods zigZagEncode :: Signed Int64 -> ZigZagEncoded Int64 Source # zigZagDecode :: ZigZagEncoded Int64 -> Signed Int64 Source # | |||||
Documentation
This class captures those types whose names need to appear in .proto files.
It has a default implementation for any data type which is an instance of the
Generic class, which will extract the name of the type constructor.
Minimal complete definition
Nothing
class ProtoEnum a => Finite a where Source #
Enumerable types with finitely many values.
This class can be derived whenever a sum type is an instance of Generic,
and only consists of zero-argument constructors. The derived instance should
be compatible with ProtoEnum instances, in the sense that
map (fromJust . toProtoEnumMay . snd) enumerate
should enumerate all values of the type without runtime errors.
Minimal complete definition
Nothing
message :: (Message a, Named a) => Proxy# a -> DotProtoDefinition Source #
Generate metadata for a message type.
enum :: (Finite e, Named e) => Proxy# e -> DotProtoDefinition Source #
Generate metadata for an enum type.
Generic Classes
class GenericMessage (f :: Type -> Type) where Source #
Methods
genericEncodeMessage :: FieldNumber -> f a -> MessageBuilder Source #
genericDecodeMessage :: FieldNumber -> Parser RawMessage (f a) Source #
genericDotProto :: Proxy# f -> [DotProtoField] Source #