proto3-suite-0.4.0.0: src/Proto3/Suite/Tutorial.hs
-- |
-- = Tutorial
--
-- >>> :set -XOverloadedStrings
--
-- This module contains a worked example of encoding and decoding messages,
-- and exporting a corresponding .proto file from Haskell types.
--
-- == Setup
--
-- If you are using "GHC.Generics", you should enable the generic deriving
-- extension, and import the main module:
--
-- > {-# LANGUAGE DeriveGeneric #-}
--
-- > import Proto3.Suite
-- > import GHC.Generics
--
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module Proto3.Suite.Tutorial where
import Data.Int (Int32)
import Data.Word (Word32)
import GHC.Exts (Proxy#, proxy#)
import GHC.Generics
import Proto3.Suite (Enumerated, Nested, NestedVec, PackedVec,
Message, Named, Finite, DotProtoDefinition,
enum, message, packageFromDefs, toProtoFileDef)
import Proto3.Wire.Class (ProtoEnum)
-- |
-- == Defining Message Types
--
-- Define messages using Haskell record types. You can use any 'MessageField' types
-- in your records, and the correct serializer and deserializer will be generated
-- for you.
--
-- Make sure to derive a 'Generic' instance for your type, and then derive instances
-- for 'Message' and 'Named' using the default (empty) instances:
--
-- > instance Message Foo
-- > instance Named Foo
--
-- == Encoding Messages
--
-- Now we can encode a value of type 'Foo' using 'Proto3.Suite.toLazyByteString'.
--
-- For example:
--
-- >>> Proto3.Suite.toLazyByteString (Foo 42 (Proto3.Suite.PackedVec (pure 123)))
-- "\b*\DC2\SOH{"
--
-- We can also decode messages using `fromByteString`:
--
-- >>> Proto3.Suite.fromByteString "\b*\DC2\SOH{" :: Either [Proto3.Suite..Decode.Parser.ParseError] Foo
-- Right (Foo {fooX = 42, fooY = PackedVec {packedvec = [123]}})
data Foo = Foo
{ fooX :: Word32
, fooY :: PackedVec Int32
} deriving (Show, Eq, Generic)
instance Message Foo
instance Named Foo
-- |
-- == Nested Messages
--
-- Messages can contain other messages, by using the 'Nested' constructor, and
-- lists of nested messages using the 'NestedVec constructor'.
data Bar = Bar
{ barShape :: Enumerated Shape
, barFoo :: Nested Foo
, foos :: NestedVec Foo
}
deriving (Eq, Generic)
instance Message Bar
instance Named Bar
-- |
-- == Enumerations
--
-- Enumerated types can be used by deriving the 'Bounded', 'Enum', 'ProtoEnum',
-- 'Finite', and 'Named' classes. Each of these instances are implied by
-- a 'Generic' instance, so can be derived as follows:
--
-- > data Shape
-- > = Circle
-- > | Square
-- > | Triangle
-- > deriving (Bounded, Eq, Enum, Finite, Generic, Named, Ord, ProtoEnum)
data Shape
= Circle
| Square
| Triangle
deriving (Bounded, Eq, Enum, Finite, Generic, Named, Ord, ProtoEnum)
-- |
-- == Generating a .proto file
--
-- We can generate a .proto file for the 'Foo' and 'Bar' data types by
-- using the 'toProtoFileDef' function. We have to provide a package name, and
-- explicitly list the message and enumeration types as a 'DotProto' value.
--
-- >>> putStrLn protoFile
-- syntax = "proto3";
-- package examplePackageName;
-- enum Shape {
-- Circle = 0;
-- Square = 1;
-- Triangle = 2;
-- }
-- message Foo {
-- uint32 fooX = 1;
-- repeated int32 fooY = 2 [packed=true];
-- }
-- message Bar {
-- Shape barShape = 1;
-- Foo barFoo = 2;
-- repeated Foo foos = 3 [packed=false];
-- }
protoFile :: String
protoFile = toProtoFileDef $ packageFromDefs "examplePackageName"
([ enum (proxy# :: Proxy# Shape)
, message (proxy# :: Proxy# Foo)
, message (proxy# :: Proxy# Bar)
] :: [DotProtoDefinition])