-- |
-- Module: Capnp.Tutorial
-- Description: Tutorial for the Haskell Cap'N Proto library.
--
-- This module provides a tutorial on the overall usage of the library. Note that
-- it does not aim to provide a thorough introduction to capnproto itself; see
-- <https://capnproto.org> for general information.
--
-- Each of the example programs described here can also be found in the @examples/@
-- subdirectory in the source repository.
{-# OPTIONS_GHC -Wno-unused-imports #-}
module Capnp.Tutorial (
    -- * Overview
    -- $overview

    -- * Setup
    -- $setup

    -- * Serialization
    -- $serialization

    -- ** High Level API
    -- $highlevel

    -- *** Example
    -- $highlevel-example

    -- *** Code Generation Rules
    -- $highlevel-codegen-rules

    -- ** Low Level API
    -- $lowlevel

    -- *** Example
    -- $lowlevel-example

    -- *** Write Support
    -- $lowlevel-write

    -- * RPC
    -- $rpc
    ) where

-- So haddock references work:
import System.IO (stdout)

import qualified Data.ByteString as BS
import qualified Data.Text       as T

import Capnp

import Capnp.Classes (FromStruct)

-- $overview
--
-- This module provides an overview of the capnp library.

-- $setup
--
-- In order to generate code from schema files, you will first need to make
-- sure the @capnp@ and @capnpc-haskell@ binaries are in your @$PATH@. The
-- former ships with the capnproto reference implementation; see
-- <https://capnproto.org/install.html>. The latter is included with this
-- library; to install it you can run the command:
--
-- > cabal v2-install capnp --installdir=$DIR
--
-- which will compile the package and create the @capnpc-haskell@ executable
-- at @$DIR/capnpc-haskell@.

-- $serialization
--
-- The serialization API is roughly divided into two parts: a low level API
-- and a high level API. The high level API eschews some of the benefits of
-- the wire format in favor of a more convenient interface.

-- $highlevel
--
-- The high level API exposes capnproto values as regular algebraic data
-- types.
--
-- On the plus side:
--
-- * This makes it easier to work with capnproto values using idiomatic
--   Haskell code
-- * Because we have to parse the data up-front we can *validate* the data
--   up front, so (unlike the low level API), you will not have to deal with
--   errors while traversing the message.
--
-- Both of these factors make the high level API generally more pleasant
-- to work with and less error-prone than the low level API.
--
-- The downside is that you can't take advantage of some of the novel
-- properties of the wire format. In particular:
--
-- * It is theoretically slower, as there is a marshalling step involved
--   (actual performance has not been measured).
-- * You can't mmap a file and read in only part of it.
-- * You can't modify a message in-place.

-- $highlevel-example
--
-- As a running example, we'll use the following schema (borrowed from the
-- C++ implementation's documentation):
--
-- > # addressbook.capnp
-- > @0xcd6db6afb4a0cf5c;
-- >
-- > struct Person {
-- >   id @0 :UInt32;
-- >   name @1 :Text;
-- >   email @2 :Text;
-- >   phones @3 :List(PhoneNumber);
-- >
-- >   struct PhoneNumber {
-- >     number @0 :Text;
-- >     type @1 :Type;
-- >
-- >     enum Type {
-- >       mobile @0;
-- >       home @1;
-- >       work @2;
-- >     }
-- >   }
-- >
-- >   employment :union {
-- >     unemployed @4 :Void;
-- >     employer @5 :Text;
-- >     school @6 :Text;
-- >     selfEmployed @7 :Void;
-- >     # We assume that a person is only one of these.
-- >   }
-- > }
-- >
-- > struct AddressBook {
-- >   people @0 :List(Person);
-- > }
--
-- Once the @capnp@ and @capnpc-haskell@ executables are installed and in
-- your @$PATH@ (see the Setup section above), you can generate code for
-- this schema by running:
--
-- > capnp compile -ohaskell addressbook.capnp
--
-- This will create the following files relative to the current directory:
--
-- * Capnp\/Gen\/Addressbook.hs
-- * Capnp\/Gen\/Addressbook\/Pure.hs
-- * Capnp\/Gen\/ById\/Xcd6db6afb4a0cf5c/Pure.hs
-- * Capnp\/Gen\/ById\/Xcd6db6afb4a0cf5c.hs
--
-- The modules under @ById@ are an implementation detail.
-- @Capnp\/Gen\/Addressbook.hs@ is generated code for use with the low level API.
-- @Capnp\/Gen\/Addressbook\/Pure.hs@ is generated code for use with the high
-- level API. The latter will export the following data declarations (cleaned up
-- for readability).
--
-- > module Capnp.Gen.Addressbook.Pure where
-- >
-- > import Data.Int
-- > import Data.Text   (Text)
-- > import Data.Vector (Vector)
-- > import Data.Word
-- >
-- > data AddressBook = AddressBook
-- >     { people :: Vector Person
-- >     }
-- >
-- > data Person = Person
-- >     { id         :: Word32
-- >     , name       :: Text
-- >     , email      :: Text
-- >     , phones     :: Vector Person'PhoneNumber
-- >     , employment :: Person'employment
-- >     }
-- >
-- > data Person'PhoneNumber = Person'PhoneNumber
-- >     { number :: Text
-- >     , type_  :: Person'PhoneNumber'Type
-- >     }
-- >
-- > data Person'employment
-- >     = Person'employment'unemployed
-- >     | Person'employment'employer Text
-- >     | Person'employment'school Text
-- >     | Person'employment'selfEmployed
-- >     | Person'employment'unknown' Word16
-- >
-- > data Person'PhoneNumber'Type
-- >     = Person'PhoneNumber'Type'mobile
-- >     | Person'PhoneNumber'Type'home
-- >     | Person'PhoneNumber'Type'work
-- >     | Person'PhoneNumber'Type'unknown' Word16
--
-- Note that we use the single quote character as a namespace separator for
-- namespaces within a single capnproto schema.
--
-- The module also exports instances of several type classes:
--
-- * 'Show'
-- * 'Read'
-- * 'Eq'
-- * 'Generic' from "GHC.Generics"
-- * 'Default' from the @data-default@ package.
-- * A number of type classes defined by the @capnp@ package.
-- * Capnproto enums additionally implement the 'Enum' type class.
--
-- Using the @Default@ instance to construct values means that your
-- existing code will continue to work if new fields are added in the
-- schema, but it also makes it easier to forget to set a field if you had
-- intended to. The instance maps @'def'@ to the default value as defined by
-- capnproto, so leaving out newly-added fields will do The Right Thing.
--
-- The module "Capnp" exposes the most frequently used
-- functionality from the capnp package. We can write an address book
-- message to standard output using the high-level API like so:
--
-- > {-# LANGUAGE OverloadedStrings     #-}
-- > -- Note that DuplicateRecordFields is usually needed, as the generated
-- > -- code relys on it to resolve collisions in capnproto struct field
-- > -- names:
-- > {-# LANGUAGE DuplicateRecordFields #-}
-- > import Capnp.Gen.Addressbook.Pure
-- >
-- > -- Note that Capnp re-exports `def`, as a convienence
-- > import Capnp (putValue, def)
-- >
-- > import qualified Data.Vector as V
-- >
-- > main = putValue AddressBook
-- >     { people = V.fromList
-- >         [ Person
-- >             { id = 123
-- >             , name = "Alice"
-- >             , email = "alice@example.com"
-- >             , phones = V.fromList
-- >                 [ def
-- >                     { number = "555-1212"
-- >                     , type_ =  Person'PhoneNumber'Type'mobile
-- >                     }
-- >                 ]
-- >             , employment = Person'employment'school "MIT"
-- >             }
-- >         , Person
-- >             { id = 456
-- >             , name = "Bob"
-- >             , email = "bob@example.com"
-- >             , phones = V.fromList
-- >                 [ def
-- >                     { number = "555-4567"
-- >                     , type_ = Person'PhoneNumber'Type'home
-- >                     }
-- >                 , def
-- >                     { number = "555-7654"
-- >                     , type_ = Person'PhoneNumber'Type'work
-- >                     }
-- >                 ]
-- >             , employment = Person'employment'selfEmployed
-- >             }
-- >         ]
-- >     }
--
-- 'putValue' is equivalent to @'hPutValue' 'stdout'@; 'hPutValue' may be used
-- to write to an arbitrary handle.
--
-- We can use 'getValue' (or alternately 'hGetValue') to read in a message:
--
-- > -- ...
-- >
-- > import Capnp (getValue, defaultLimit)
-- >
-- > -- ...
-- >
-- > main = do
-- >     value <- getValue defaultLimit
-- >     print (value :: AddressBook)
--
-- Note the type annotation; there are a number of interfaces in the
-- library which dispatch on return types, and depending on how they are
-- used you may have to give GHC a hint for type inference to succeed.
-- The type of 'getValue' is:
--
-- @'getValue' :: 'FromStruct' 'ConstMsg' a => 'Int' -> 'IO' a@
--
-- ...and so it may be used to read in any struct type.
--
-- 'defaultLimit' is a default value for the traversal limit, which acts to
-- prevent denial of service vulnerabilities; See the documentation in
-- "Capnp.TraversalLimit" for more information. 'getValue' uses this
-- argument both to catch values that would cause excessive resource usage,
-- and to simply limit the overall size of the incoming message. The
-- default is approximately 64 MiB.
--
-- If an error occurs, an exception will be thrown of type 'Error' from the
-- "Capnp.Errors" module.

-- $highlevel-codegen-rules
--
-- The complete rules for how capnproto types map to Haskell are as follows:
--
-- * Integer types and booleans map to the obvious corresponding Haskell
--   types.
-- * @Float32@ and @Float64@ map to 'Float' and 'Double', respectively.
-- * @Void@ maps to the unit type, @()@.
-- * Lists map to 'Vector's from the Haskell vector package. Note that
--   right now we use boxed vectors for everything; at some point this will
--   likely change for performance reasons. Using the functions from
--   "Data.Vector.Generic" will probably decrease the amount of code you
--   will need to modify when upgrading.
-- * @Text@ maps to (strict) 'T.Text' from the Haskell `text` package.
-- * @Data@ maps to (strict) 'BS.ByteString's
-- * Type constructor names are the fully qualified (within the schema file)
--   capnproto name, using the single quote character as a namespace
--   separator.
-- * Structs map to record types. The name of the data constructor is the
--   same as the name of the type constructor.
-- * Groups are treated mostly like structs, except that the data constructor
--   (but not the type constructor) has an extra trailing single-quote. This
--   is to avoid name collisions that would otherwise be possible.
-- * Field names map to record fields with the same names. Names that are
--   Haskell keywords have an underscore appended to them, e.g. @type_@ in
--   the above example. These names are not qualified; we use the
--   @DuplicateRecordFields@ extension to disambiguate them.
-- * Union fields result in an auxiliary type definition named
--   @\<containing type's name>'\<union field name>@. For an example, see the
--   mapping of the `employment` field above.
-- * Unions and enums map to sum types, each of which has a special
--   `unknown'` variant (note the trailing single quote). This variant will
--   be returned when parsing a message which contains a union tag greater
--   than what was defined in the schema. This is most likely to happen
--   when dealing with data generated by software using a newer version
--   of the same schema. The argument to the data constructor is the value
--   of the tag.
-- * Union variants with arguments of type `Void` map to data constructors
--   with no arguments.
-- * The type for an anonymous union has the same name as its containing
--   struct with an extra single quote on the end. You can think of this as
--   being like a field with the empty string as its name. The Haskell
--   record accessor for this field is named `union'` (note the trailing
--   single quote).
-- * As a special case, if a struct consists entirely of one anonymous
--   union, the type for the struct itself is omitted, and the name of the
--   type for the union does not have the trailing single quote (so its
--   name is what the name of the struct type would be).
-- * Fields of type `AnyPointer` map to the types defined in
--   @Capnp.Untyped.Pure@.
-- * Interfaces generate associated type classes and client types; see
--   the section on RPC.

-- $lowlevel
--
-- The low level API exposes a much more imperative interface than the
-- high-level API. Instead of algebraic data types, types are exposed as
-- opaque wrappers around references into a message, and accessors are
-- generated for the fields. This API is much closer in spirit to that of
-- the C++ reference implementation.
--
-- Because the low level interfaces do not parse and validate the message
-- up front, accesses to the message can result in errors. Furthermore, the
-- traversal limit needs to be tracked to avoid denial of service attacks.
--
-- Because of this, access to the message must occur inside of a monad
-- which is an instance of `MonadThrow` from the exceptions package, and
-- `MonadLimit`, which is defined in "Capnp.TraversalLimit". We define
-- a monad transformer `LimitT` for the latter.

-- $lowlevel-example
--
-- We'll use the same schema as above for our example. Instead of standard
-- algebraic data types, the module `Capnp.Addressbook` primarily defines
-- newtype wrappers, which should be treated as opaque, and accessor
-- functions for the various fields.
--
-- @
-- newtype AddressBook msg = ...
--
-- get_Addressbook'people :: ReadCtx m msg => AddressBook msg -> m (List msg (Person msg))
--
-- newtype Person msg = ...
--
-- get_Person'id   :: ReadCtx m msg => Person msg -> m Word32
-- get_Person'name :: ReadCtx m msg => Person msg -> m (Text msg)
-- @
--
-- `ReadCtx` is a type synonym:
--
-- @
-- type ReadCtx m msg = (Message m msg, MonadThrow m, MonadLimit m)
-- @
--
-- Note the following:
--
-- * The generated data types are parametrized over a `msg` type. This is
--   the type of the message in which the value is contained. This can be
--   either 'ConstMsg' in the case of an immutable message, or @'MutMsg' s@
--   for a mutable message (where `s` is the state token for the monad in
--   which the message may be mutated).
-- * The `Text` and `List` types mentioned in the type signatures are types
--   defined within the capnp library, and are similarly views into the
--   underlying message.
-- * Access to the message happens in a monad which affords throwing
--   exceptions, tracking the traversal limit, and of course reading the
--   message.
--
-- The snippet below prints the names of each person in the address book:
--
-- > {-# LANGUAGE ScopedTypeVariables #-}
-- > import Prelude hiding (length)
-- >
-- > import Capnp.Gen.Addressbook
-- > import Capnp
-- >     (ConstMsg, defaultLimit, evalLimitT, getValue, index, length, textBytes)
-- >
-- > import           Control.Monad         (forM_)
-- > import           Control.Monad.Trans   (lift)
-- > import qualified Data.ByteString.Char8 as BS8
-- >
-- > main = do
-- >     addressbook :: AddressBook ConstMsg <- getValue defaultLimit
-- >     evalLimitT defaultLimit $ do
-- >         people <- get_AddressBook'people addressbook
-- >         forM_ [0..length people - 1] $ \i -> do
-- >             name <- index i people >>= get_Person'name >>= textBytes
-- >             lift $ BS8.putStrLn name
--
-- Note that we use the same `getValue` function as in the high-level
-- example above.

-- $lowlevel-write
--
-- Writing messages using the low-level API has a similarly imperative feel.
-- The below constructs the same message as in our high-level example above:
--
-- > import Capnp.Gen.Addressbook
-- >
-- > import Capnp
-- >     ( MutMsg
-- >     , PureBuilder
-- >     , cerialize
-- >     , createPure
-- >     , defaultLimit
-- >     , index
-- >     , newMessage
-- >     , newRoot
-- >     , putMsg
-- >     )
-- >
-- > import qualified Data.Text as T
-- >
-- > main =
-- >     let Right msg = createPure defaultLimit buildMsg
-- >     in putMsg msg
-- >
-- > buildMsg :: PureBuilder s (MutMsg s)
-- > buildMsg = do
-- >     -- newMessage allocates a new, initially empty, mutable message:
-- >     msg <- newMessage
-- >
-- >     -- newRoot allocates a new struct as the root object of the message.
-- >     -- In this case the type of the struct can be inferred from our later
-- >     -- use of AddressBook's accessors:
-- >     addressbook <- newRoot msg
-- >
-- >     -- new_* accessors allocate a new value of the correct type for a
-- >     -- given field. These functions accordingly only exist for types
-- >     -- which are encoded as pointers (structs, lists, bytes...). In
-- >     -- the case of lists, these take an extra argument specifying a
-- >     -- the length of the list:
-- >     people <- new_AddressBook'people 2 addressbook
-- >
-- >     -- Index gets an object at a specified location in a list. Cap'N Proto
-- >     -- lists are flat arrays, and in the case of structs the structs are
-- >     -- unboxed, so there is no need to allocate each element:
-- >     alice <- index 0 people
-- >
-- >     -- set_* functions set the value of a field. For fields of non-pointer
-- >     -- types (integers, bools...), We can just pass the value we want to set_*,
-- >     -- rather than allocating via new_* first:
-- >     set_Person'id alice 123
-- >
-- >     -- 'cerialize' is used to marshal a value into a message. Below, we copy
-- >     -- the text for Alice's name and email address into the message, and then
-- >     -- use Person's set_* functions to attach the resulting objects to our
-- >     -- Person:
-- >     set_Person'name alice =<< cerialize msg (T.pack "Alice")
-- >     set_Person'email alice =<< cerialize msg (T.pack "alice@example.com")
-- >
-- >     phones <- new_Person'phones 1 alice
-- >     mobilePhone <- index 0 phones
-- >     set_Person'PhoneNumber'number mobilePhone =<< cerialize msg (T.pack "555-1212")
-- >     set_Person'PhoneNumber'type_ mobilePhone Person'PhoneNumber'Type'mobile
-- >
-- >     -- Setting union fields is slightly awkward; we have an auxiliary type
-- >     -- for the union field, which we must get_* first:
-- >     employment <- get_Person'employment alice
-- >
-- >     -- Then, we can use set_* to set both the tag of the union and the
-- >     -- value:
-- >     set_Person'employment'school employment =<< cerialize msg (T.pack "MIT")
-- >
-- >     bob <- index 1 people
-- >     set_Person'id bob 456
-- >     set_Person'name bob =<< cerialize msg (T.pack "Bob")
-- >     set_Person'email bob =<< cerialize msg (T.pack "bob@example.com")
-- >
-- >     phones <- new_Person'phones 2 bob
-- >     homePhone <- index 0 phones
-- >     set_Person'PhoneNumber'number homePhone =<< cerialize msg (T.pack "555-4567")
-- >     set_Person'PhoneNumber'type_ homePhone Person'PhoneNumber'Type'home
-- >     workPhone <- index 1 phones
-- >     set_Person'PhoneNumber'number workPhone =<< cerialize msg (T.pack "555-7654")
-- >     set_Person'PhoneNumber'type_ workPhone Person'PhoneNumber'Type'work
-- >     employment <- get_Person'employment bob
-- >     set_Person'employment'selfEmployed employment
-- >
-- >     pure msg

-- $rpc
--
-- This package supports level 1 Cap'n Proto RPC. The tuotrial will demonstrate the most
-- basic features of the RPC system with example: an echo server & client. For a larger
-- example which demos more of the protocol's capabilities, see the calculator example
-- in the source repository's @examples/@ directory.
--
-- Given the schema:
--
-- > @0xd0a87f36fa0182f5;
-- >
-- > interface Echo {
-- >   echo @0 (query :Text) -> (reply :Text);
-- > }
--
-- In the low level module, the code generator generates a newtype wrapper called @Echo@
-- around a capability.
--
-- Most of the interesting stuff is in the high-level module (but note that you can still
-- do RPC using low-level serialization APIs). The code generator will create an API like
-- (after a bit of cleanup):
--
-- > newtype Echo = Echo Client
-- >
-- > class MonadIO m => Echo'server_ m cap where
-- >     echo'echo :: cap -> Server.MethodHandler m Echo'echo'params Echo'echo'results
-- >
-- > instance Echo'server_ IO Echo
-- >
-- > export_Echo :: Echo'server_ IO a => Supervisors -> a -> STM Echo
--
-- The type @Echo@ is a handle to an object (possibly remote), which can be used to
-- make method calls. It is a newtype wrapper around a 'Client', which provides
-- similar facilities, but doesn't know about the schema.
--
-- To provide an implementation of the @Echo@ interface, you need an instance of the
-- @Echo'server_@ type class. The @export_Echo@ function is used to convert such an
-- instance into a handle to the object that can be passed around.
--
-- Each time you call @export_Function@, it creates a thread that handles incoming
-- messages in sequence.
--
-- Note that capnproto does not have a notion of "clients" and "servers" in the
-- traditional networking sense; the two sides of a connection are symmetric. In
-- capnproto terminology, a "client" is a handle for calling methods, and a "server"
-- is an object that handles methods -- but there may be many of either or both of
-- these on each side of a connection.
--
-- Here is an an echo (networking) server using this interface:
--
-- > {-# LANGUAGE MultiParamTypeClasses #-}
-- > {-# LANGUAGE OverloadedStrings     #-}
-- > import Network.Simple.TCP (serve)
-- >
-- > import Capnp     (def, defaultLimit)
-- > -- 'Capnp.Rpc' exposes the most commonly used parts of the RPC system:
-- > import Capnp.Rpc
-- >     (ConnConfig(..), handleConn, pureHandler, socketTransport, toClient)
-- >
-- > import Capnp.Gen.Echo.Pure
-- >
-- > -- | A type to declare an instance on:
-- > data MyEchoServer = MyEchoServer
-- >
-- > -- The main logic of an echo server:
-- > instance Echo'server_ IO MyEchoServer where
-- >     -- Each method of an interface generates a corresponding
-- >     -- method in its type class. The name of the method is prefixed
-- >     -- with the name of the interface, so method bar on interface
-- >     -- Foo will be called foo'bar.
-- >     --
-- >     -- The type of a method is left abstract, and functions like
-- >     -- 'pureHandler' are used to construct method handlers; see the
-- >     -- "Handling method calls" section in the docs for 'Capnp.Rpc'.
-- >     echo'echo = pureHandler $ \MyEchoServer params ->
-- >         pure def { reply = query params }
-- >
-- > main :: IO ()
-- > main = serve "localhost" "4000" $ \(sock, _addr) ->
-- >     -- once we get a network connection, we use 'handleConn' to start
-- >     -- the rpc subsystem on that connection. It takes a transport with
-- >     -- which to send messages, and a config.
-- >     handleConn (socketTransport sock defaultLimit) def
-- >         { getBootstrap = \sup ->
-- >            -- The only setting we override in this example is our
-- >            -- bootstrap interface. The bootstrap interface is a "default"
-- >            -- object that clients can request on startup. By default
-- >            -- there is none, here we provide a client for our echo server.
-- >            Just . toClient <$> export_Echo sup MyEchoServer
-- >         }
--
-- The echo client looks like:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- > module Examples.Rpc.EchoClient (main) where
-- >
-- > import Network.Simple.TCP (connect)
-- >
-- > import Capnp     (def, defaultLimit)
-- > import Capnp.Rpc (ConnConfig(..), handleConn, socketTransport, wait, (?))
-- >
-- > import Capnp.Gen.Echo.Pure
-- >
-- > main :: IO ()
-- > main = connect "localhost" "4000" $ \(sock, _addr) ->
-- >     handleConn (socketTransport sock defaultLimit) def
-- >         -- In this case, we leave 'getBootstrap' empty and set
-- >         -- 'withBootstrap', which will request the other side's
-- >         -- bootstrap interface. If a non-Nothing value is supplied for
-- >         -- 'withBootstrap', 'handleConn' will exit (and disconnect)
-- >         -- when it completes.
-- >         { withBootstrap = Just $ \_sup client ->
-- >             -- Clients also have instances of their server_ classes, so
-- >             -- can use these instances to call methods on the remote
-- >             -- object. The '?' is the message send operator.
-- >             --
-- >             -- The method call _immediately_ returns, yielding a promise
-- >             -- that will be fulfilled when the results of the call actually
-- >             -- arive. We use 'wait' to wait for the promise to resolve,
-- >             -- display the result to the user, and then exit.
-- >             echo'echo (Echo client) ? def { query = "Hello, World!" }
-- >                 >>= wait
-- >                 >>= print
-- >         }