{-
  Copyright 2016 Awake Networks

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-}
-- | This module is an executable tutorial for the @proto3-wire@ library.
-- It will demonstrate how to encode and decode messages of various types.
--
-- = Imports
--
-- We recommend importing the "Proto3.Wire.Encode" and "Proto3.Wire.Decode"
-- modules qualified, since they define encoding and decoding functions with the
-- same names.
--
-- The "Proto3.Wire" module reexports some useful functions, so a good default
-- set of imports is:
--
-- > import           Proto3.Wire
-- > import qualified Proto3.Wire.Encode as Encode
-- > import qualified Proto3.Wire.Decode as Decode
--
-- = Primitives
--
-- Let's translate this simple @.proto@ file into a Haskell data type and a pair
-- of encoding and decoding functions:
--
-- > message EchoRequest {
-- >   string message = 1;
-- > }
--
-- We begin by defining a data type to represent our messages:
--
-- > data EchoRequest = EchoRequest { echoRequestMessage :: Text }
--
-- == Encoding
--
-- To encode an 'EchoRequest', we use the @Encode.'Encode.text'@ function, and provide
-- the field number and the text value:
--
-- > encodeEchoRequest :: EchoRequest -> Encode.MessageBuilder
-- > encodeEchoRequest EchoRequest{..} =
-- >     Encode.text 1 echoRequestMessage
--
-- Fields of type @string@ can be encoded\/decoded from\/to values of type 'String',
-- 'ByteString' and 'Text'. Here we use the 'Text' type, which is encoded using
-- the 'Encode.text' function. Different primitive types have different encoding
-- functions, which are usually named after the Protocol Buffers type.
--
-- == Decoding
--
-- To decode an 'EchoRequest', we use the 'Decode.parse' function, and provide
-- a 'Decode.Parser' to extract the fields:
--
-- > decodeEchoRequest :: ByteString -> Either Decode.ParseError EchoRequest
-- > decodeEchoRequest = Decode.parse echoRequestParser
--
-- The decoding function for 'Text' is called @Decode.'Decode.text'@. However, we must
-- specify the field number, which is done using the `at` function, and provide
-- a default value, using the `one` function. The types will ensure that
-- the field number and default value are provided.
--
-- We use the 'Functor' instance for 'Decode.Parser' to apply the 'EchoRequest'
-- constructor to the result:
--
-- > echoRequestParser :: Decode.Parser Decode.RawMessage EchoRequest
-- > echoRequestParser = EchoRequest <$> (one Decode.text mempty `at` 1
--
-- = Messages with multiple fields
--
-- Let's make our example more interesting by including multiple fields:
--
-- > message EchoResponse {
-- >   string message = 1;
-- >   uint64 timestamp = 2;
-- > }
--
-- We begin by defining a data type to represent our messages:
--
-- > data EchoResponse = EchoResponse { echoResponseMessage   :: Text
-- >                                  , echoResponseTimestamp :: Word64
-- >                                  }
--
-- == Encoding
--
-- To encode messages with multiple fields, note that functions in the
-- "Proto3.Wire.Encode" module return values in the 'Encode.MessageBuilder'
-- monoid, so we can use `mappend` to combine messages:
--
-- > encodedEchoResponse :: EchoResponse -> Encode.MessageBuilder
-- > encodedEchoResponse EchoResponse{..} =
-- >     Encode.text 1 echoResponseMessage <>
-- >         Encode.uint64 2 echoResponseTimestamp
--
-- However, be careful to always use increasing field numbers, since this is not
-- enforced by the library.
--
-- == Decoding
--
-- Messages with many fields can be parsed using the 'Applicative' instance for
-- 'Parser':
--
-- > decodeEchoResponse :: ByteString -> Either Decode.ParseError EchoResponse
-- > decodeEchoResponse = Decode.parse echoResponseParser
-- >
-- > echoResponseParser :: Decode.Parser Decode.RawMessage EchoResponse
-- > echoResponseParser = EchoResponse <$> (one Decode.text mempty `at` 1)
-- >                                   <*> (one Decode.uint64 0 `at` 2)
--
-- = Repeated Fields and Embedded Messages
--
-- Messages can be embedded in fields of other messages. This can be useful
-- when entire sections of a message can be repeated or omitted.
--
-- Consider the following message types:
--
-- > message EchoManyRequest {
-- >   repeated EchoRequest requests = 1;
-- > }
--
-- Again, we define a type corresponding to our message:
--
-- > data EchoManyRequest = EchoManyRequest { echoManyRequestRequests :: Seq EchoRequest }
--
-- == Encoding
--
-- Messages can be embedded using `Encode.embedded`.
--
-- In protocol buffers version 3, all fields are optional. To omit a value for a
-- field, simply do not append it to the 'Encode.MessageBuilder'.
--
-- Similarly, repeated fields can be encoded by concatenating several values
-- with the same 'FieldNumber'.
--
-- It can be useful to use 'foldMap' to deal with these cases.
--
-- > encodeEchoManyRequest :: EchoManyRequest -> Encode.MessageBuilder
-- > encodeEchoManyRequest =
-- >   foldMap (Encode.embedded 1 . encodeEchoRequest)
-- >   . echoManyRequestRequests
--
-- == Decoding
--
-- Embedded messages can be decoded using 'Decode.embedded'.
--
-- Repeated fields can be decoded using 'repeated'.
--
-- Repeated embedded messages can be decoded using @repeated . Decode.embedded'@.
--
-- > decodeEchoManyRequest :: ByteString -> Either Decode.ParseError EchoManyRequest
-- > decodeEchoManyRequest = Decode.parse echoManyRequestParser
-- >
-- > echoManyRequestParser :: Decode.Parser Decode.RawMessage EchoManyRequest
-- > echoManyRequestParser =
-- >   EchoManyRequest <$> (repeated (Decode.embedded' echoRequestParser) `at` 1)
{-# LANGUAGE RecordWildCards #-}

module Proto3.Wire.Tutorial where

import           Data.ByteString         ( ByteString )
import           Data.Monoid             ( (<>) )
import           Data.Text.Lazy          ( Text )
import           Data.Word               ( Word64 )

import           Proto3.Wire
import qualified Proto3.Wire.Encode      as Encode
import qualified Proto3.Wire.Decode      as Decode

data EchoRequest = EchoRequest { EchoRequest -> Text
echoRequestMessage :: Text }

encodeEchoRequest :: EchoRequest -> Encode.MessageBuilder
encodeEchoRequest :: EchoRequest -> MessageBuilder
encodeEchoRequest EchoRequest{Text
echoRequestMessage :: Text
echoRequestMessage :: EchoRequest -> Text
..} =
    FieldNumber -> Text -> MessageBuilder
Encode.text FieldNumber
1 Text
echoRequestMessage

decodeEchoRequest :: ByteString -> Either Decode.ParseError EchoRequest
decodeEchoRequest :: ByteString -> Either ParseError EchoRequest
decodeEchoRequest = Parser RawMessage EchoRequest
-> ByteString -> Either ParseError EchoRequest
forall a. Parser RawMessage a -> ByteString -> Either ParseError a
Decode.parse Parser RawMessage EchoRequest
echoRequestParser

echoRequestParser :: Decode.Parser Decode.RawMessage EchoRequest
echoRequestParser :: Parser RawMessage EchoRequest
echoRequestParser = Text -> EchoRequest
EchoRequest (Text -> EchoRequest)
-> Parser RawMessage Text -> Parser RawMessage EchoRequest
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser RawPrimitive Text -> Text -> Parser RawField Text
forall a. Parser RawPrimitive a -> a -> Parser RawField a
one Parser RawPrimitive Text
Decode.text Text
forall a. Monoid a => a
mempty Parser RawField Text -> FieldNumber -> Parser RawMessage Text
forall a. Parser RawField a -> FieldNumber -> Parser RawMessage a
`at` FieldNumber
1)

data EchoResponse = EchoResponse { EchoResponse -> Text
echoResponseMessage   :: Text
                                 , EchoResponse -> Word64
echoResponseTimestamp :: Word64
                                 }

encodedEchoResponse :: EchoResponse -> Encode.MessageBuilder
encodedEchoResponse :: EchoResponse -> MessageBuilder
encodedEchoResponse EchoResponse{Word64
Text
echoResponseTimestamp :: Word64
echoResponseMessage :: Text
echoResponseTimestamp :: EchoResponse -> Word64
echoResponseMessage :: EchoResponse -> Text
..} =
    FieldNumber -> Text -> MessageBuilder
Encode.text FieldNumber
1 Text
echoResponseMessage MessageBuilder -> MessageBuilder -> MessageBuilder
forall a. Semigroup a => a -> a -> a
<>
        FieldNumber -> Word64 -> MessageBuilder
Encode.uint64 FieldNumber
2 Word64
echoResponseTimestamp

decodeEchoResponse :: ByteString -> Either Decode.ParseError EchoResponse
decodeEchoResponse :: ByteString -> Either ParseError EchoResponse
decodeEchoResponse = Parser RawMessage EchoResponse
-> ByteString -> Either ParseError EchoResponse
forall a. Parser RawMessage a -> ByteString -> Either ParseError a
Decode.parse Parser RawMessage EchoResponse
echoResponseParser

echoResponseParser :: Decode.Parser Decode.RawMessage EchoResponse
echoResponseParser :: Parser RawMessage EchoResponse
echoResponseParser = Text -> Word64 -> EchoResponse
EchoResponse (Text -> Word64 -> EchoResponse)
-> Parser RawMessage Text
-> Parser RawMessage (Word64 -> EchoResponse)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser RawPrimitive Text -> Text -> Parser RawField Text
forall a. Parser RawPrimitive a -> a -> Parser RawField a
one Parser RawPrimitive Text
Decode.text Text
forall a. Monoid a => a
mempty Parser RawField Text -> FieldNumber -> Parser RawMessage Text
forall a. Parser RawField a -> FieldNumber -> Parser RawMessage a
`at` FieldNumber
1)
                                  Parser RawMessage (Word64 -> EchoResponse)
-> Parser RawMessage Word64 -> Parser RawMessage EchoResponse
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Parser RawPrimitive Word64 -> Word64 -> Parser RawField Word64
forall a. Parser RawPrimitive a -> a -> Parser RawField a
one Parser RawPrimitive Word64
Decode.uint64 Word64
0 Parser RawField Word64 -> FieldNumber -> Parser RawMessage Word64
forall a. Parser RawField a -> FieldNumber -> Parser RawMessage a
`at` FieldNumber
2)

data EchoManyRequest = EchoManyRequest { EchoManyRequest -> [EchoRequest]
echoManyRequestRequests ::  [EchoRequest]
                                       }

encodeEchoManyRequest :: EchoManyRequest -> Encode.MessageBuilder
encodeEchoManyRequest :: EchoManyRequest -> MessageBuilder
encodeEchoManyRequest = (EchoRequest -> MessageBuilder) -> [EchoRequest] -> MessageBuilder
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (FieldNumber -> MessageBuilder -> MessageBuilder
Encode.embedded FieldNumber
1 (MessageBuilder -> MessageBuilder)
-> (EchoRequest -> MessageBuilder) -> EchoRequest -> MessageBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                                     EchoRequest -> MessageBuilder
encodeEchoRequest) ([EchoRequest] -> MessageBuilder)
-> (EchoManyRequest -> [EchoRequest])
-> EchoManyRequest
-> MessageBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    EchoManyRequest -> [EchoRequest]
echoManyRequestRequests

decodeEchoManyRequest :: ByteString -> Either Decode.ParseError EchoManyRequest
decodeEchoManyRequest :: ByteString -> Either ParseError EchoManyRequest
decodeEchoManyRequest = Parser RawMessage EchoManyRequest
-> ByteString -> Either ParseError EchoManyRequest
forall a. Parser RawMessage a -> ByteString -> Either ParseError a
Decode.parse Parser RawMessage EchoManyRequest
echoManyRequestParser

echoManyRequestParser :: Decode.Parser Decode.RawMessage EchoManyRequest
echoManyRequestParser :: Parser RawMessage EchoManyRequest
echoManyRequestParser = [EchoRequest] -> EchoManyRequest
EchoManyRequest ([EchoRequest] -> EchoManyRequest)
-> Parser RawMessage [EchoRequest]
-> Parser RawMessage EchoManyRequest
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser RawPrimitive EchoRequest -> Parser RawField [EchoRequest]
forall a. Parser RawPrimitive a -> Parser RawField [a]
repeated (Parser RawMessage EchoRequest -> Parser RawPrimitive EchoRequest
forall a. Parser RawMessage a -> Parser RawPrimitive a
Decode.embedded' Parser RawMessage EchoRequest
echoRequestParser) Parser RawField [EchoRequest]
-> FieldNumber -> Parser RawMessage [EchoRequest]
forall a. Parser RawField a -> FieldNumber -> Parser RawMessage a
`at` FieldNumber
1)