Run stylish-haskell in all files! 💅🏼 (#23)

* Run stylish-haskell in all files! 💅🏼
This commit is contained in:
Flavio Corpa 2019-11-21 13:25:53 +01:00 committed by GitHub
parent 2cd09028a2
commit e0910afd5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 997 additions and 768 deletions

10
.editorconfig Normal file
View File

@ -0,0 +1,10 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

4
.gitignore vendored
View File

@ -1,5 +1,5 @@
stack*.yaml.lock
.*
.stack-work
*~
dist
*.pyc
*.pyc

63
.stylish-haskell.yaml Normal file
View File

@ -0,0 +1,63 @@
steps:
- simple_align:
cases: true
top_level_patterns: true
records: true
# Import cleanup
- imports:
align: global
list_align: after_alias
pad_module_names: true
long_list_align: inline
empty_list_align: inherit
list_padding: 4
separate_lists: true
space_surround: false
# Language pragmas
- language_pragmas:
style: vertical
align: true
remove_redundant: true
language_prefix: language
# Remove trailing whitespace
- trailing_whitespace: {}
columns: 100
newline: native
cabal: true
language_extensions:
- BangPatterns
- ConstraintKinds
- DataKinds
- DefaultSignatures
- DeriveAnyClass
- DeriveDataTypeable
- DeriveGeneric
- DerivingStrategies
- DerivingVia
- ExplicitNamespaces
- FlexibleContexts
- FlexibleInstances
- FunctionalDependencies
- GADTs
- GeneralizedNewtypeDeriving
- InstanceSigs
- KindSignatures
- LambdaCase
- MultiParamTypeClasses
- MultiWayIf
- NamedFieldPuns
- NoImplicitPrelude
- OverloadedStrings
- QuasiQuotes
- RecordWildCards
- ScopedTypeVariables
- StandaloneDeriving
- TemplateHaskell
- TupleSections
- TypeApplications
- TypeFamilies
- ViewPatterns

26
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,26 @@
# Development recommendations
Before continuing, make sure you've read:
- [Alejandro's post on setting up a Haskell development environment](https://www.47deg.com/blog/setting-up-haskell/).
## VSCode extensions
To make our lives easier while developing in Haskell, we use the following extensions:
- [ghcide](https://marketplace.visualstudio.com/items?itemName=DigitalAssetHoldingsLLC.ghcide), the best thing that happened to Haskell for editors/IDEs! ❤️
- [hlint](https://marketplace.visualstudio.com/items?itemName=hoovercj.haskell-linter), another great extension to have suggestions and refactors in Haskell 🛠
- [stylish-haskell](https://marketplace.visualstudio.com/items?itemName=vigoo.stylish-haskell), the formatter we use to prettify the code 💅🏼
- [editorconfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig), to have consistency between different editors and envs 🐀
## stylish-haskell 💅🏼
Regarding the formatter, we use the `master` version of [stylish-haskell](https://github.com/jaspervdj/stylish-haskell) to be able to use language pragmas with lowercase, so you'll need to do this locally:
```sh
$ git clone https://github.com/jaspervdj/stylish-haskell
$ ...
$ cd stylish-haskell && stack install
```
Happy hacking! 👏🏼

View File

@ -1,30 +1,35 @@
{-# language PolyKinds, DataKinds, GADTs,
FlexibleInstances, FlexibleContexts,
TypeApplications, TypeOperators,
ScopedTypeVariables, RankNTypes,
MultiParamTypeClasses,
UndecidableInstances #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Mu.Adapter.Avro where
import Control.Arrow ((***))
import qualified Data.Avro as A
import qualified Data.Avro.Schema as ASch
import qualified Data.Avro.Types.Value as AVal
import Control.Arrow ((***))
import qualified Data.Avro as A
import qualified Data.Avro.Schema as ASch
import qualified Data.Avro.Types.Value as AVal
-- 'Tagged . unTagged' can be replaced by 'coerce'
-- eliminating some run-time overhead
import Data.Coerce (coerce)
import qualified Data.HashMap.Strict as HM
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmptyList
import qualified Data.Map as M
import Data.SOP (NP(..), NS(..))
import Data.Tagged
import qualified Data.Text as T
import qualified Data.Vector as V
import GHC.TypeLits
import Data.Coerce (coerce)
import qualified Data.HashMap.Strict as HM
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NonEmptyList
import qualified Data.Map as M
import Data.SOP (NP (..), NS (..))
import Data.Tagged
import qualified Data.Text as T
import qualified Data.Vector as V
import GHC.TypeLits
import Mu.Schema
import Mu.Schema
import qualified Mu.Schema.Interpretation.Schemaless as SLess
instance SLess.ToSchemalessTerm (AVal.Value t) where
@ -69,7 +74,7 @@ instance HasAvroSchemas sch sch
instance (HasSchema sch sty t, HasAvroSchemas sch sch, A.FromAvro (Term sch (sch :/: sty)))
=> A.FromAvro (WithSchema sch sty t) where
fromAvro (AVal.Union _ _ v) = WithSchema . fromSchema' @sch <$> A.fromAvro v
fromAvro v = ASch.badValue v "top-level"
fromAvro v = ASch.badValue v "top-level"
instance (HasSchema sch sty t, HasAvroSchemas sch sch, A.ToAvro (Term sch (sch :/: sty)))
=> A.ToAvro (WithSchema sch sty t) where
toAvro (WithSchema v) = AVal.Union (schemas (Proxy @sch) (Proxy @sch))
@ -164,11 +169,11 @@ instance (KnownName name, HasAvroSchemaEnum fs)
instance (KnownName name, HasAvroSchemaFields sch args, FromAvroFields sch args)
=> A.FromAvro (Term sch ('DRecord name anns args)) where
fromAvro (AVal.Record _ fields) = TRecord <$> fromAvroF fields
fromAvro v = A.badValue v "record"
fromAvro v = A.badValue v "record"
instance (KnownName name, HasAvroSchemaEnum choices, FromAvroEnum choices)
=> A.FromAvro (Term sch ('DEnum name anns choices)) where
fromAvro v@(AVal.Enum _ n _) = TEnum <$> fromAvroEnum v n
fromAvro v = A.badValue v "enum"
fromAvro v = A.badValue v "enum"
instance A.FromAvro (FieldValue sch t)
=> A.FromAvro (Term sch ('DSimple t)) where
fromAvro v = TSimple <$> A.fromAvro v
@ -184,7 +189,7 @@ instance (KnownName t, A.FromAvro (Term sch (sch :/: t)))
instance (HasAvroSchemaUnion (FieldValue sch) choices, FromAvroUnion sch choices)
=> A.FromAvro (FieldValue sch ('TUnion choices)) where
fromAvro (AVal.Union _ branch v) = FUnion <$> fromAvroU branch v
fromAvro v = A.badValue v "union"
fromAvro v = A.badValue v "union"
instance A.FromAvro (FieldValue sch t)
=> A.FromAvro (FieldValue sch ('TOption t)) where
fromAvro v = FOption <$> A.fromAvro v
@ -258,7 +263,7 @@ instance forall sch choices.
toAvro (FUnion v) = AVal.Union wholeSchema' chosenTy chosenVal
where wholeSchema = schemaU (Proxy @(FieldValue sch)) (Proxy @choices)
wholeSchema' = V.fromList (NonEmptyList.toList wholeSchema)
(chosenTy, chosenVal) = toAvroU v
(chosenTy, chosenVal) = toAvroU v
instance A.ToAvro (FieldValue sch t)
=> A.ToAvro (FieldValue sch ('TOption t)) where
toAvro (FOption v) = A.toAvro v
@ -306,4 +311,4 @@ instance (KnownName name, A.ToAvro (FieldValue sch t), ToAvroFields sch fs)
nameText :: KnownName s => proxy s -> T.Text
nameText = T.pack . nameVal
nameTypeName :: KnownName s => proxy s -> ASch.TypeName
nameTypeName = ASch.parseFullname . nameText
nameTypeName = ASch.parseFullname . nameText

View File

@ -3,7 +3,7 @@
module Mu.Adapter.Avro.Example where
import Mu.Quasi.Avro (avro, avroFile)
import Mu.Quasi.Avro (avro, avroFile)
type Example = [avro|
{

View File

@ -12,13 +12,13 @@ module Mu.Quasi.Avro (
, schemaFromAvroType
) where
import Data.Aeson (decode)
import qualified Data.Avro.Schema as A
import qualified Data.ByteString as B
import Data.ByteString.Lazy.Char8 (pack)
import Data.Aeson (decode)
import qualified Data.Avro.Schema as A
import qualified Data.ByteString as B
import Data.ByteString.Lazy.Char8 (pack)
import Data.Int
import qualified Data.Text as T
import Data.Vector (fromList, toList)
import qualified Data.Text as T
import Data.Vector (fromList, toList)
import Language.Haskell.TH
import Language.Haskell.TH.Quote
@ -43,66 +43,75 @@ schemaFromAvroString s =
Nothing -> fail "could not parse avro spec!"
Just (A.Union us) -> schemaFromAvro (toList us)
Just t -> schemaFromAvro [t]
where schemaFromAvro = (typesToList <$>) . mapM schemaDecFromAvroType . flattenAvroDecls
where
schemaFromAvro =
(typesToList <$>) . mapM schemaDecFromAvroType . flattenAvroDecls
schemaDecFromAvroType :: A.Type -> Q Type
schemaDecFromAvroType (A.Record name _ _ _ fields) =
[t|'DRecord $(textToStrLit $ A.baseName name) '[] $(typesToList <$> mapM avroFieldToType fields)|]
where
[t|'DRecord $(textToStrLit $ A.baseName name) '[] $(typesToList <$>
mapM
avroFieldToType
fields)|]
where
avroFieldToType :: A.Field -> Q Type
avroFieldToType field =
[t|'FieldDef $(textToStrLit $ A.fldName field) '[] $(schemaFromAvroType $ A.fldType field)|]
[t|'FieldDef $(textToStrLit $ A.fldName field) '[] $(schemaFromAvroType $
A.fldType field)|]
schemaDecFromAvroType (A.Enum name _ _ symbols) =
[t|'DEnum $(textToStrLit $ A.baseName name) '[] $(typesToList <$> mapM avChoiceToType (toList symbols))|]
[t|'DEnum $(textToStrLit $ A.baseName name) '[] $(typesToList <$>
mapM
avChoiceToType
(toList symbols))|]
where
avChoiceToType :: T.Text -> Q Type
avChoiceToType c = [t|'ChoiceDef $(textToStrLit c) '[]|]
schemaDecFromAvroType t = [t| 'DSimple $(schemaFromAvroType t) |]
schemaDecFromAvroType t = [t|'DSimple $(schemaFromAvroType t)|]
schemaFromAvroType :: A.Type -> Q Type
schemaFromAvroType = \case
A.Null -> [t|'TPrimitive 'TNull|]
A.Boolean -> [t|'TPrimitive Bool|]
A.Int -> [t|'TPrimitive Int32|]
A.Long -> [t|'TPrimitive Int64|]
A.Float -> [t|'TPrimitive Float|]
A.Double -> [t|'TPrimitive Double|]
A.Bytes -> [t|'TPrimitive B.ByteString|]
A.String -> [t|'TPrimitive T.Text|]
A.Array item -> [t|'TList $(schemaFromAvroType item)|]
A.Map values -> [t|'TMap T.Text $(schemaFromAvroType values)|]
A.NamedType typeName ->
[t|'TSchematic $(textToStrLit (A.baseName typeName))|]
A.Enum {} -> fail "should never happen, please, file an issue"
A.Record {} -> fail "should never happen, please, file an issue"
A.Union options ->
case toList options of
[A.Null, x] -> toOption x
[x, A.Null] -> toOption x
_ -> [t|'TUnion $(typesToList <$> mapM schemaFromAvroType (toList options))|]
where toOption x = [t|'TOption $(schemaFromAvroType x)|]
A.Fixed {} -> fail "fixed integers are not currently supported"
schemaFromAvroType =
\case
A.Null -> [t|'TPrimitive 'TNull|]
A.Boolean -> [t|'TPrimitive Bool|]
A.Int -> [t|'TPrimitive Int32|]
A.Long -> [t|'TPrimitive Int64|]
A.Float -> [t|'TPrimitive Float|]
A.Double -> [t|'TPrimitive Double|]
A.Bytes -> [t|'TPrimitive B.ByteString|]
A.String -> [t|'TPrimitive T.Text|]
A.Array item -> [t|'TList $(schemaFromAvroType item)|]
A.Map values -> [t|'TMap T.Text $(schemaFromAvroType values)|]
A.NamedType typeName ->
[t|'TSchematic $(textToStrLit (A.baseName typeName))|]
A.Enum {} -> fail "should never happen, please, file an issue"
A.Record {} -> fail "should never happen, please, file an issue"
A.Union options ->
case toList options of
[A.Null, x] -> toOption x
[x, A.Null] -> toOption x
_ ->
[t|'TUnion $(typesToList <$> mapM schemaFromAvroType (toList options))|]
where toOption x = [t|'TOption $(schemaFromAvroType x)|]
A.Fixed {} -> fail "fixed integers are not currently supported"
flattenAvroDecls :: [A.Type] -> [A.Type]
flattenAvroDecls = concatMap (uncurry (:) . flattenDecl)
where
flattenDecl :: A.Type -> (A.Type, [A.Type])
flattenDecl (A.Record name a d o fields) =
flattenDecl (A.Record name a d o fields) =
let (flds, tts) = unzip (flattenAvroField <$> fields)
in (A.Record name a d o flds, concat tts)
flattenDecl (A.Union _) = error "should never happen, please, file an issue"
in (A.Record name a d o flds, concat tts)
flattenDecl (A.Union _) = error "should never happen, please, file an issue"
flattenDecl t = (t, [])
flattenAvroType :: A.Type -> (A.Type, [A.Type])
flattenAvroType (A.Record name a d o fields) =
flattenAvroType (A.Record name a d o fields) =
let (flds, tts) = unzip (flattenAvroField <$> fields)
in (A.NamedType name, A.Record name a d o flds : concat tts)
flattenAvroType (A.Union (toList -> ts)) =
in (A.NamedType name, A.Record name a d o flds : concat tts)
flattenAvroType (A.Union (toList -> ts)) =
let (us, tts) = unzip (map flattenAvroType ts)
in (A.Union $ fromList us, concat tts)
in (A.Union $ fromList us, concat tts)
flattenAvroType e@A.Enum {A.name} = (A.NamedType name, [e])
flattenAvroType t = (t, [])
flattenAvroField :: A.Field -> (A.Field, [A.Type])
flattenAvroField f =
let (t, decs) = flattenAvroType (A.fldType f)

View File

@ -1,16 +1,18 @@
{-# language OverloadedStrings, TypeApplications,
NamedFieldPuns, DataKinds,
StandaloneDeriving, DerivingVia #-}
{-# language DataKinds #-}
{-# language DerivingVia #-}
{-# language OverloadedStrings #-}
{-# language StandaloneDeriving #-}
{-# language TypeApplications #-}
{-# options_ghc -fno-warn-orphans #-}
module Main where
import Data.Avro
import Data.Avro
import qualified Data.ByteString.Lazy as BS
import System.Environment
import System.Environment
import Mu.Schema (WithSchema(..))
import Mu.Adapter.Avro ()
import Mu.Schema.Examples
import Mu.Adapter.Avro ()
import Mu.Schema (WithSchema (..))
import Mu.Schema.Examples
exampleAddress :: Address
exampleAddress = Address "1111BB" "Spain"
@ -20,19 +22,19 @@ examplePerson1 = Person "Haskellio" "Gómez" (Just 30) (Just Male) exampleAddres
examplePerson2 = Person "Cuarenta" "Siete" Nothing Nothing exampleAddress
deriving via (WithSchema ExampleSchema "person" Person) instance HasAvroSchema Person
deriving via (WithSchema ExampleSchema "person" Person) instance FromAvro Person
deriving via (WithSchema ExampleSchema "person" Person) instance ToAvro Person
deriving via (WithSchema ExampleSchema "person" Person) instance FromAvro Person
deriving via (WithSchema ExampleSchema "person" Person) instance ToAvro Person
main :: IO ()
main = do -- Obtain the filenames
[genFile, conFile] <- getArgs
-- Read the file produced by Python
putStrLn "haskell/consume"
cbs <- BS.readFile conFile
let [people] = decodeContainer @Person cbs
print people
-- Encode a couple of values
putStrLn "haskell/generate"
print [examplePerson1, examplePerson2]
gbs <- encodeContainer [[examplePerson1, examplePerson2]]
BS.writeFile genFile gbs
[genFile, conFile] <- getArgs
-- Read the file produced by Python
putStrLn "haskell/consume"
cbs <- BS.readFile conFile
let [people] = decodeContainer @Person cbs
print people
-- Encode a couple of values
putStrLn "haskell/generate"
print [examplePerson1, examplePerson2]
gbs <- encodeContainer [[examplePerson1, examplePerson2]]
BS.writeFile genFile gbs

View File

@ -1,11 +1,17 @@
{-# language PolyKinds, DataKinds, GADTs,
TypeFamilies, TypeOperators,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
ScopedTypeVariables, TypeApplications,
UndecidableInstances,
OverloadedStrings, ConstraintKinds,
AllowAmbiguousTypes #-}
{-# language AllowAmbiguousTypes #-}
{-# language ConstraintKinds #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language OverloadedStrings #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Mu.Adapter.ProtoBuf (
-- * Custom annotations
@ -23,23 +29,23 @@ module Mu.Adapter.ProtoBuf (
, parseProtoBufWithRegistry
) where
import Control.Applicative
import qualified Data.ByteString as BS
import Data.Int
import Data.Kind
import Data.SOP (All)
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import GHC.TypeLits
import Proto3.Wire
import qualified Proto3.Wire.Encode as PBEnc
import qualified Proto3.Wire.Decode as PBDec
import Control.Applicative
import qualified Data.ByteString as BS
import Data.Int
import Data.Kind
import Data.SOP (All)
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import GHC.TypeLits
import Proto3.Wire
import qualified Proto3.Wire.Decode as PBDec
import qualified Proto3.Wire.Encode as PBEnc
import Mu.Schema.Annotations
import Mu.Schema.Definition
import Mu.Schema.Interpretation
import Mu.Schema.Class
import qualified Mu.Schema.Registry as R
import Mu.Schema.Annotations
import Mu.Schema.Class
import Mu.Schema.Definition
import Mu.Schema.Interpretation
import qualified Mu.Schema.Registry as R
type family FindProtoBufId (f :: fn) (xs :: [Type]) :: Nat where
FindProtoBufId f '[]
@ -51,7 +57,7 @@ type family FindProtoBufOneOfIds (f :: fn) (xs :: [Type]) :: [Nat] where
FindProtoBufOneOfIds f '[]
= TypeError ('Text "protocol buffers ids not available for oneof field " ':<>: 'ShowType f)
FindProtoBufOneOfIds f (ProtoBufOneOfIds n ': rest) = n
FindProtoBufOneOfIds f (other ': rest) = FindProtoBufOneOfIds f rest
FindProtoBufOneOfIds f (other ': rest) = FindProtoBufOneOfIds f rest
-- CONVERSION USING SCHEMAS
@ -78,13 +84,13 @@ parseProtoViaSchema = PBDec.parse (fromProtoViaSchema @_ @_ @sch)
-- CONVERSION USING REGISTRY
fromProtoBufWithRegistry
:: forall (r :: R.Registry) t.
:: forall (r :: R.Registry) t.
FromProtoBufRegistry r t
=> PBDec.Parser PBDec.RawMessage t
fromProtoBufWithRegistry = fromProtoBufRegistry' (Proxy @r)
parseProtoBufWithRegistry
:: forall (r :: R.Registry) t.
:: forall (r :: R.Registry) t.
FromProtoBufRegistry r t
=> BS.ByteString -> Either PBDec.ParseError t
parseProtoBufWithRegistry = PBDec.parse (fromProtoBufWithRegistry @r)
@ -148,7 +154,7 @@ instance (All (ProtoBridgeField sch) args, ProtoBridgeFields sch args)
termToProto (TRecord fields) = go fields
where go :: forall fs. All (ProtoBridgeField sch) fs
=> NP (Field sch) fs -> PBEnc.MessageBuilder
go Nil = mempty
go Nil = mempty
go (f :* fs) = fieldToProto f <> go fs
protoToTerm = TRecord <$> protoToFields
@ -361,4 +367,4 @@ instance ( ProtoBridgeFieldValue sch t, KnownNat thisId
unionFieldValueToProto (S v) = unionFieldValueToProto @_ @_ @restIds v
protoToUnionFieldValue
= Z <$> protoToFieldValue `at` fieldId <|> S <$> protoToUnionFieldValue @_ @_ @restIds
where fieldId = fromInteger $ natVal (Proxy @thisId)
where fieldId = fromInteger $ natVal (Proxy @thisId)

View File

@ -1,7 +1,8 @@
{-# language QuasiQuotes, DataKinds #-}
{-# language DataKinds #-}
{-# language QuasiQuotes #-}
module Mu.Adapter.ProtoBuf.Example where
import Mu.Quasi.ProtoBuf
import Mu.Quasi.ProtoBuf
type ExampleProtoBufSchema = [protobuf|
enum gender {
@ -14,4 +15,4 @@ message person {
int age = 2;
gender gender = 3;
}
|]
|]

View File

@ -1,20 +1,23 @@
{-# language PolyKinds, DataKinds,
MultiParamTypeClasses,
ScopedTypeVariables, TypeApplications,
FlexibleInstances, FlexibleContexts,
UndecidableInstances #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-simplifiable-class-constraints -fno-warn-orphans #-}
module Mu.Adapter.ProtoBuf.Via where
import Network.GRPC.HTTP2.Proto3Wire
import qualified Proto3.Wire.Encode as PBEnc
import qualified Proto3.Wire.Decode as PBDec
import Network.GRPC.HTTP2.Proto3Wire
import qualified Proto3.Wire.Decode as PBDec
import qualified Proto3.Wire.Encode as PBEnc
import Mu.Rpc
import Mu.Schema
import Mu.Adapter.ProtoBuf
import Mu.Adapter.ProtoBuf
import Mu.Rpc
import Mu.Schema
newtype ViaProtoBufTypeRef (ref :: TypeRef) t
newtype ViaProtoBufTypeRef (ref :: TypeRef) t
= ViaProtoBufTypeRef { unViaProtoBufTypeRef :: t }
instance ProtoBufTypeRef ref t
@ -39,4 +42,4 @@ instance ( FromProtoBufRegistry r t
, HasProtoSchema (MappingRight r last) sty t)
=> ProtoBufTypeRef ('FromRegistry r t last) t where
fromProtoBufTypeRef _ = fromProtoBufWithRegistry @r
toProtoBufTypeRef _ = toProtoViaSchema @_ @_ @(MappingRight r last)
toProtoBufTypeRef _ = toProtoViaSchema @_ @_ @(MappingRight r last)

View File

@ -1,21 +1,23 @@
{-# language TemplateHaskell, DataKinds, OverloadedStrings #-}
{-# language DataKinds #-}
{-# language OverloadedStrings #-}
{-# language TemplateHaskell #-}
-- | Read a @.proto@ file as a 'Service'
module Mu.Quasi.GRpc (
grpc
, compendium
) where
import Control.Monad.IO.Class
import qualified Data.Text as T
import Language.Haskell.TH
import qualified Language.ProtocolBuffers.Types as P
import Language.ProtocolBuffers.Parser
import Network.HTTP.Client
import Servant.Client.Core.BaseUrl
import Control.Monad.IO.Class
import qualified Data.Text as T
import Language.Haskell.TH
import Language.ProtocolBuffers.Parser
import qualified Language.ProtocolBuffers.Types as P
import Network.HTTP.Client
import Servant.Client.Core.BaseUrl
import Mu.Quasi.ProtoBuf
import Mu.Rpc
import Compendium.Client
import Compendium.Client
import Mu.Quasi.ProtoBuf
import Mu.Rpc
-- | Reads a @.proto@ file and generates:
-- * A 'Schema' with all the message types, using the
@ -88,7 +90,7 @@ pbMethodToType s (P.Method nm vr v rr r _)
= [t| 'RetStream ('FromSchema $(schemaTy s) $(textToStrLit (last a))) |]
retToType _ _
= fail "only message types may be used as results"
schemaTy :: Name -> Q Type
schemaTy schema = return $ ConT schema
@ -97,4 +99,4 @@ typesToList
= foldr (\y ys -> AppT (AppT PromotedConsT y) ys) PromotedNilT
textToStrLit :: T.Text -> Q Type
textToStrLit s
= return $ LitT $ StrTyLit $ T.unpack s
= return $ LitT $ StrTyLit $ T.unpack s

View File

@ -2,7 +2,6 @@
{-# language LambdaCase #-}
{-# language NamedFieldPuns #-}
{-# language TemplateHaskell #-}
{-# language ViewPatterns #-}
module Mu.Quasi.ProtoBuf (
-- * Quasi-quoters for @.proto@ files
@ -83,22 +82,22 @@ pbTypeDeclToType (P.DMessage name _ _ fields _) =
= [t| 'FieldDef $(textToStrLit nm)
'[ ProtoBufOneOfIds $(typesToList <$> mapM (intToLit . getFieldNumber) vs ) ]
$(typesToList <$> mapM pbOneOfFieldToType vs ) |]
pbFieldTypeToType :: P.FieldType -> Q Type
pbFieldTypeToType P.TInt32 = [t|'TPrimitive Int32|]
pbFieldTypeToType P.TUInt32 = fail "unsigned integers are not currently supported"
pbFieldTypeToType P.TSInt32 = [t|'TPrimitive Int32|]
pbFieldTypeToType P.TInt64 = [t|'TPrimitive Int64|]
pbFieldTypeToType P.TUInt64 = fail "unsigned integers are not currently supported"
pbFieldTypeToType P.TSInt64 = [t|'TPrimitive Int64|]
pbFieldTypeToType P.TFixed32 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TFixed64 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TSFixed32 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TSFixed64 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TDouble = [t|'TPrimitive Double|]
pbFieldTypeToType P.TBool = [t|'TPrimitive Bool|]
pbFieldTypeToType P.TString = [t|'TPrimitive T.Text|]
pbFieldTypeToType P.TBytes = [t|'TPrimitive B.ByteString|]
pbFieldTypeToType P.TInt32 = [t|'TPrimitive Int32|]
pbFieldTypeToType P.TUInt32 = fail "unsigned integers are not currently supported"
pbFieldTypeToType P.TSInt32 = [t|'TPrimitive Int32|]
pbFieldTypeToType P.TInt64 = [t|'TPrimitive Int64|]
pbFieldTypeToType P.TUInt64 = fail "unsigned integers are not currently supported"
pbFieldTypeToType P.TSInt64 = [t|'TPrimitive Int64|]
pbFieldTypeToType P.TFixed32 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TFixed64 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TSFixed32 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TSFixed64 = fail "fixed integers are not currently supported"
pbFieldTypeToType P.TDouble = [t|'TPrimitive Double|]
pbFieldTypeToType P.TBool = [t|'TPrimitive Bool|]
pbFieldTypeToType P.TString = [t|'TPrimitive T.Text|]
pbFieldTypeToType P.TBytes = [t|'TPrimitive B.ByteString|]
pbFieldTypeToType (P.TOther t) = [t|'TSchematic $(textToStrLit (last t))|]
hasFieldNumber P.NormalField {} = True

View File

@ -1,15 +1,17 @@
{-# language OverloadedStrings, TypeApplications, ScopedTypeVariables #-}
{-# language OverloadedStrings #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
module Main where
import qualified Data.ByteString as BS
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Proto3.Wire.Decode as PBDec
import qualified Proto3.Wire.Encode as PBEnc
import System.Environment
import qualified Proto3.Wire.Decode as PBDec
import qualified Proto3.Wire.Encode as PBEnc
import System.Environment
import Mu.Schema ()
import Mu.Adapter.ProtoBuf
import Mu.Schema.Examples
import Mu.Adapter.ProtoBuf
import Mu.Schema ()
import Mu.Schema.Examples
exampleAddress :: Address
exampleAddress = Address "1111BB" "Spain"
@ -20,14 +22,14 @@ examplePerson2 = Person "Cuarenta" "Siete" Nothing Nothing exampleAddress
main :: IO ()
main = do -- Obtain the filenames
[genFile, conFile] <- getArgs
-- Read the file produced by Python
putStrLn "haskell/consume"
cbs <- BS.readFile conFile
let Right people = PBDec.parse (fromProtoViaSchema @_ @_ @ExampleSchema) cbs
print (people :: Person)
-- Encode a couple of values
putStrLn "haskell/generate"
print examplePerson1
let gbs = PBEnc.toLazyByteString (toProtoViaSchema @_ @_ @ExampleSchema examplePerson1)
LBS.writeFile genFile gbs
[genFile, conFile] <- getArgs
-- Read the file produced by Python
putStrLn "haskell/consume"
cbs <- BS.readFile conFile
let Right people = PBDec.parse (fromProtoViaSchema @_ @_ @ExampleSchema) cbs
print (people :: Person)
-- Encode a couple of values
putStrLn "haskell/generate"
print examplePerson1
let gbs = PBEnc.toLazyByteString (toProtoViaSchema @_ @_ @ExampleSchema examplePerson1)
LBS.writeFile genFile gbs

View File

@ -12,17 +12,21 @@ maintainer: alejandro.serrano@47deg.com
-- copyright:
category: Network
build-type: Simple
-- extra-source-files: README.md, CHANGELOG.md
extra-source-files: README.md, CHANGELOG.md
library
exposed-modules: Compendium.Client
-- other-modules:
-- other-extensions:
build-depends: base >=4.12 && <5,
aeson, text,
http-client,
servant, servant-client,
megaparsec, language-protobuf
build-depends: base >=4.12 && <5
, aeson
, text
, http-client
, servant
, servant-client
, megaparsec
, language-protobuf
hs-source-dirs: src
default-language: Haskell2010
ghc-options: -Wall -fprint-potential-instances
ghc-options: -Wall
-fprint-potential-instances

View File

@ -1,30 +1,33 @@
{-# language DataKinds, TypeOperators,
DeriveGeneric, DeriveAnyClass,
ViewPatterns, TypeApplications #-}
{-# language DataKinds #-}
{-# language DeriveAnyClass #-}
{-# language DeriveGeneric #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
{-# language ViewPatterns #-}
module Compendium.Client where
import Data.Aeson
import Data.Char
import Data.Proxy
import Data.Text
import Language.ProtocolBuffers.Types
import Language.ProtocolBuffers.Parser
import Network.HTTP.Client (Manager)
import Servant.API
import Servant.Client
import Text.Megaparsec
import Data.Aeson
import Data.Char
import Data.Proxy
import Data.Text
import Language.ProtocolBuffers.Parser
import Language.ProtocolBuffers.Types
import Network.HTTP.Client (Manager)
import Servant.API
import Servant.Client
import Text.Megaparsec
import GHC.Generics
import GHC.Generics
newtype Protocol
= Protocol { raw :: Text }
deriving (Eq, Show, Generic, FromJSON)
data IdlName
= Avro | Protobuf | Mu | OpenApi | Scala
deriving (Eq, Show, Generic)
instance ToHttpApiData IdlName where
toQueryParam (show -> x:xs)
toQueryParam (show -> x:xs)
= pack $ Data.Char.toLower x : xs
toQueryParam _ = error "this should never happen"
@ -50,12 +53,12 @@ data ObtainProtoBufError
obtainProtoBuf :: Manager -> BaseUrl
-> Text -> IO (Either ObtainProtoBufError ProtoBuf)
obtainProtoBuf m url ident
= do r <- transformation m url ident Protobuf
case r of
Left e
-> return $ Left (OPEClient e)
Right (Protocol p)
-> case parseProtoBuf p of
Left e -> return $ Left (OPEParse e)
Right pb -> return $ Right pb
obtainProtoBuf m url ident = do
r <- transformation m url ident Protobuf
case r of
Left e
-> return $ Left (OPEClient e)
Right (Protocol p)
-> case parseProtoBuf p of
Left e -> return $ Left (OPEParse e)
Right pb -> return $ Right pb

View File

@ -1,8 +1,11 @@
{-# language DataKinds, PolyKinds,
GADTs, ExistentialQuantification,
TypeFamilies, ConstraintKinds,
TypeOperators,
UndecidableInstances #-}
{-# language ConstraintKinds #-}
{-# language DataKinds #-}
{-# language ExistentialQuantification #-}
{-# language GADTs #-}
{-# language PolyKinds #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Protocol-independent declaration of services
module Mu.Rpc (
Service', Service(..)
@ -11,12 +14,12 @@ module Mu.Rpc (
, TypeRef(..), Argument(..), Return(..)
) where
import Data.Kind
import GHC.TypeLits
import Data.Kind
import GHC.TypeLits
import qualified Language.Haskell.TH as TH
import Mu.Schema
import Mu.Schema.Registry
import Mu.Schema
import Mu.Schema.Registry
type Service' = Service Symbol Symbol
@ -74,4 +77,4 @@ data Return where
RetThrows :: TypeRef -> TypeRef -> Return
-- | Return a stream of values
-- (this can be found in gRPC).
RetStream :: TypeRef -> Return
RetStream :: TypeRef -> Return

View File

@ -1,19 +1,25 @@
{-# language PolyKinds, DataKinds, GADTs,
MultiParamTypeClasses,
FlexibleInstances, OverloadedStrings,
DeriveGeneric, DeriveAnyClass, TypeOperators,
PartialTypeSignatures, TypeFamilies #-}
{-# language DataKinds #-}
{-# language DeriveAnyClass #-}
{-# language DeriveGeneric #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language OverloadedStrings #-}
{-# language PartialTypeSignatures #-}
{-# language PolyKinds #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Mu.Rpc.Examples where
import Data.Conduit
import Data.Conduit.Combinators as C
import qualified Data.Text as T
import GHC.Generics
import Data.Conduit
import Data.Conduit.Combinators as C
import qualified Data.Text as T
import GHC.Generics
import Mu.Schema
import Mu.Rpc
import Mu.Server
import Mu.Rpc
import Mu.Schema
import Mu.Server
-- Defines the service from gRPC Quickstart
-- https://grpc.io/docs/quickstart/python/
@ -59,4 +65,4 @@ quickstartServer
-> ConduitT HelloResponse Void IO ()
-> IO ()
sayManyHellos source sink
= runConduit $ source .| C.mapM sayHello .| sink
= runConduit $ source .| C.mapM sayHello .| sink

View File

@ -1,12 +1,14 @@
{-# language DataKinds, PolyKinds,
GADTs, TypeFamilies,
ExistentialQuantification,
MultiParamTypeClasses,
FlexibleInstances,
UndecidableInstances,
TypeOperators,
ConstraintKinds,
RankNTypes #-}
{-# language ConstraintKinds #-}
{-# language DataKinds #-}
{-# language ExistentialQuantification #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language RankNTypes #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Protocol-independent declaration of servers.
--
-- A server (represented by 'ServerIO' and in general
@ -29,11 +31,11 @@ module Mu.Server (
, HandlersIO, HandlersT(..)
) where
import Data.Conduit
import Data.Kind
import Data.Conduit
import Data.Kind
import Mu.Rpc
import Mu.Schema
import Mu.Rpc
import Mu.Schema
data ServerT (s :: Service snm mnm) (m :: Type -> Type) (hs :: [Type]) where
Server :: HandlersT methods m hs -> ServerT ('Service sname anns methods) m hs
@ -67,4 +69,4 @@ instance (HandlesRef eref e, HandlesRef vref v, handler ~ m (Either e v))
instance (HandlesRef ref v, handler ~ m v)
=> Handles '[] ('RetSingle ref) m handler
instance (HandlesRef ref v, handler ~ (ConduitT v Void m () -> m ()))
=> Handles '[] ('RetStream ref) m handler
=> Handles '[] ('RetStream ref) m handler

View File

@ -1,22 +1,26 @@
{-# language PolyKinds, DataKinds, GADTs,
TypeOperators, ScopedTypeVariables,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
TypeApplications,
UndecidableInstances #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Mu.Adapter.Json where
import Control.Applicative ((<|>))
import Data.Aeson
import Data.Aeson.Types
import Data.Functor.Contravariant
import qualified Data.HashMap.Strict as HM
import Data.SOP (NS(..), NP(..))
import qualified Data.Text as T
import qualified Data.Vector as V
import Control.Applicative ((<|>))
import Data.Aeson
import Data.Aeson.Types
import Data.Functor.Contravariant
import qualified Data.HashMap.Strict as HM
import Data.SOP (NP (..), NS (..))
import qualified Data.Text as T
import qualified Data.Vector as V
import Mu.Schema
import Mu.Schema
import qualified Mu.Schema.Interpretation.Schemaless as SLess
instance SLess.ToSchemalessTerm Value where
@ -46,7 +50,7 @@ instance ToJSONFields sch args => ToJSON (Term sch ('DRecord name anns args)) wh
toJSON (TRecord fields) = Object (toJSONFields fields)
instance FromJSONFields sch args => FromJSON (Term sch ('DRecord name anns args)) where
parseJSON (Object v) = TRecord <$> parseJSONFields v
parseJSON _ = fail "expected object"
parseJSON _ = fail "expected object"
class ToJSONFields sch fields where
toJSONFields :: NP (Field sch) fields -> Object
@ -71,7 +75,7 @@ instance ToJSONEnum choices => ToJSON (Term sch ('DEnum name anns choices)) wher
toJSON (TEnum choice) = String (toJSONEnum choice)
instance FromJSONEnum choices => FromJSON (Term sch ('DEnum name anns choices)) where
parseJSON (String s) = TEnum <$> parseJSONEnum s
parseJSON _ = fail "expected string"
parseJSON _ = fail "expected string"
class ToJSONEnum choices where
toJSONEnum :: NS Proxy choices -> T.Text
@ -132,7 +136,7 @@ instance (ToJSON (FieldValue sch u), ToJSONUnion sch us)
instance FromJSON (FieldValue sch 'TNull) where
parseJSON Null = return FNull
parseJSON _ = fail "expected null"
parseJSON _ = fail "expected null"
instance FromJSON t => FromJSON (FieldValue sch ('TPrimitive t)) where
parseJSON v = FPrimitive <$> parseJSON v
instance FromJSONKey t => FromJSONKey (FieldValue sch ('TPrimitive t)) where
@ -161,4 +165,4 @@ instance FromJSONUnion sch '[] where
unionFromJSON _ = fail "value does not match any of the types of the union"
instance (FromJSON (FieldValue sch u), FromJSONUnion sch us)
=> FromJSONUnion sch (u ': us) where
unionFromJSON v = Z <$> parseJSON v <|> S <$> unionFromJSON v
unionFromJSON v = Z <$> parseJSON v <|> S <$> unionFromJSON v

View File

@ -1,4 +1,4 @@
{-# language DataKinds #-}
{-# language DataKinds #-}
-- | Schemas for Mu microservices
module Mu.Schema (
-- * Schema definition
@ -21,7 +21,7 @@ module Mu.Schema (
, ProtoBufId, ProtoBufOneOfIds
) where
import Mu.Schema.Annotations
import Mu.Schema.Definition
import Mu.Schema.Interpretation
import Mu.Schema.Class
import Mu.Schema.Annotations
import Mu.Schema.Class
import Mu.Schema.Definition
import Mu.Schema.Interpretation

View File

@ -1,9 +1,10 @@
{-# language DataKinds, KindSignatures #-}
{-# language DataKinds #-}
{-# language KindSignatures #-}
module Mu.Schema.Annotations where
import GHC.TypeLits
import GHC.TypeLits
-- ANNOTATION FOR CONVERSION
data ProtoBufId (n :: Nat)
data ProtoBufOneOfIds (ns :: [Nat])
data ProtoBufOneOfIds (ns :: [Nat])

View File

@ -1,24 +1,30 @@
{-# language PolyKinds, DataKinds, GADTs,
TypeFamilies, TypeOperators,
FunctionalDependencies,
FlexibleInstances, FlexibleContexts,
TypeApplications, ScopedTypeVariables,
UndecidableInstances,
DefaultSignatures #-}
{-# language DataKinds #-}
{-# language DefaultSignatures #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language FunctionalDependencies #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Conversion from types to schemas
module Mu.Schema.Class (
WithSchema(..), HasSchema(..), fromSchema', toSchema'
, Mapping(..), Mappings, MappingRight, MappingLeft
) where
import Data.Kind
import Data.Map as M
import Data.SOP
import GHC.Generics
import GHC.TypeLits
import Data.Kind
import Data.Map as M
import Data.SOP
import GHC.Generics
import GHC.TypeLits
import Mu.Schema.Definition
import Mu.Schema.Interpretation
import Mu.Schema.Definition
import Mu.Schema.Interpretation
-- | Tags a value with its schema.
-- For usage with @deriving via@.
@ -126,7 +132,7 @@ instance {-# OVERLAPPABLE #-}
-- This instance removes unneeded metadata from the
-- top of the type.
instance {-# OVERLAPS #-}
GSchemaTypeDef sch fmap ('DSimple t) f
GSchemaTypeDef sch fmap ('DSimple t) f
=> GSchemaTypeDef sch fmap ('DSimple t) (D1 meta f) where
toSchemaTypeDef p (M1 x) = toSchemaTypeDef p x
fromSchemaTypeDef p x = M1 (fromSchemaTypeDef p x)
@ -160,7 +166,7 @@ instance (GSchemaFieldType sch sk hk, GSchemaFieldType sch sv hv,
Ord (FieldValue sch sk), Ord hk) -- Ord is required to build a map
=> GSchemaFieldType sch ('TMap sk sv) (M.Map hk hv) where
toSchemaFieldType x = FMap (M.mapKeys toSchemaFieldType (M.map toSchemaFieldType x))
fromSchemaFieldType (FMap x) = M.mapKeys fromSchemaFieldType (M.map fromSchemaFieldType x)
fromSchemaFieldType (FMap x) = M.mapKeys fromSchemaFieldType (M.map fromSchemaFieldType x)
-- This assumes that a union is represented by
-- a value of type 'NS', where types are in
-- the same order.
@ -173,7 +179,7 @@ instance AllZip (GSchemaFieldType sch) ts vs
go (S n) = S (go n)
fromSchemaFieldType (FUnion t) = go t
where go :: AllZip (GSchemaFieldType sch) tss vss
=> NS (FieldValue sch) tss -> NS I vss
=> NS (FieldValue sch) tss -> NS I vss
go (Z x) = Z (I (fromSchemaFieldType x))
go (S n) = S (go n)
@ -189,7 +195,7 @@ instance {-# OVERLAPPABLE #-}
-- This instance removes unneeded metadata from the
-- top of the type.
instance {-# OVERLAPS #-}
GSchemaTypeDef sch fmap ('DEnum name anns choices) f
GSchemaTypeDef sch fmap ('DEnum name anns choices) f
=> GSchemaTypeDef sch fmap ('DEnum name anns choices) (D1 meta f) where
toSchemaTypeDef p (M1 x) = toSchemaTypeDef p x
fromSchemaTypeDef p x = M1 (fromSchemaTypeDef p x)
@ -261,12 +267,12 @@ instance {-# OVERLAPPABLE #-}
-- This instance removes unneeded metadata from the
-- top of the type.
instance {-# OVERLAPS #-}
GSchemaTypeDef sch fmap ('DRecord name anns args) f
GSchemaTypeDef sch fmap ('DRecord name anns args) f
=> GSchemaTypeDef sch fmap ('DRecord name anns args) (D1 meta f) where
toSchemaTypeDef p (M1 x) = toSchemaTypeDef p x
fromSchemaTypeDef p x = M1 (fromSchemaTypeDef p x)
instance {-# OVERLAPS #-}
GSchemaTypeDef sch fmap ('DRecord name anns args) f
GSchemaTypeDef sch fmap ('DRecord name anns args) f
=> GSchemaTypeDef sch fmap ('DRecord name anns args) (C1 meta f) where
toSchemaTypeDef p (M1 x) = toSchemaTypeDef p x
fromSchemaTypeDef p x = M1 (fromSchemaTypeDef p x)
@ -297,7 +303,7 @@ instance ( GToSchemaRecord sch fmap cs f
class GToSchemaRecordSearch (sch :: Schema ts fs) (t :: FieldType ts) (f :: * -> *) (w :: Where) where
toSchemaRecordSearch :: Proxy w -> f a -> FieldValue sch t
instance GSchemaFieldType sch t v
instance GSchemaFieldType sch t v
=> GToSchemaRecordSearch sch t (S1 m (K1 i v)) 'Here where
toSchemaRecordSearch _ (M1 (K1 x)) = toSchemaFieldType x
instance GSchemaFieldType sch t v
@ -341,4 +347,4 @@ instance GSchemaFieldType sch t v => GFromSchemaRecordSearch sch v ('FieldDef na
instance forall sch v other rest n.
GFromSchemaRecordSearch sch v rest n
=> GFromSchemaRecordSearch sch v (other ': rest) ('There n) where
fromSchemaRecordSearch _ (_ :* xs) = fromSchemaRecordSearch (Proxy @n) xs
fromSchemaRecordSearch _ (_ :* xs) = fromSchemaRecordSearch (Proxy @n) xs

View File

@ -1,20 +1,23 @@
{-# language CPP, TemplateHaskell, TypeOperators, DataKinds #-}
{-# language CPP #-}
{-# language DataKinds #-}
{-# language TemplateHaskell #-}
{-# language TypeOperators #-}
-- | Generate a set of Haskell types from a 'Schema'.
module Mu.Schema.Conversion.SchemaToTypes (
generateTypesFromSchema
, Namer
) where
import Control.Applicative
import Data.Char
import qualified Data.Map as M
import Data.SOP
import GHC.Generics (Generic)
import Language.Haskell.TH
import Language.Haskell.TH.Datatype
import Control.Applicative
import Data.Char
import qualified Data.Map as M
import Data.SOP
import GHC.Generics (Generic)
import Language.Haskell.TH
import Language.Haskell.TH.Datatype
import Mu.Schema.Definition
import Mu.Schema.Class
import Mu.Schema.Class
import Mu.Schema.Definition
-- | Generate the name from each new Haskell type
-- from the name given in the schema.
@ -141,11 +144,11 @@ fieldName :: String -> String -> String
fieldName complete fname = firstLower (complete ++ firstUpper fname)
firstUpper :: String -> String
firstUpper [] = error "Empty names are not allowed"
firstUpper [] = error "Empty names are not allowed"
firstUpper (x:rest) = toUpper x : rest
firstLower :: String -> String
firstLower [] = error "Empty names are not allowed"
firstLower [] = error "Empty names are not allowed"
firstLower (x:rest) = toLower x : rest
fieldTypeToDecl :: Namer -> FieldTypeB Type String -> Type
@ -180,8 +183,8 @@ typeToSchemaDef toplevelty
typeToSchemaDef' expanded
= do types <- tyList expanded
mapM typeToTypeDef types
typeToTypeDef, typeToRecordDef, typeToEnumDef, typeToSimpleType
typeToTypeDef, typeToRecordDef, typeToEnumDef, typeToSimpleType
:: Type -> Maybe (TypeDefB Type String String)
typeToTypeDef t
= typeToRecordDef t <|> typeToEnumDef t <|> typeToSimpleType t
@ -269,4 +272,4 @@ tyD3 name (SigT t _) = tyD3 name t
tyD3 name (AppT (AppT (AppT (PromotedT c) x) y) z)
| c == name = Just (x, y, z)
| otherwise = Nothing
tyD3 _ _ = Nothing
tyD3 _ _ = Nothing

View File

@ -1,8 +1,10 @@
{-# language PolyKinds, DataKinds, TypeFamilies,
TypeOperators,
UndecidableInstances #-}
{-# language DataKinds #-}
{-# language PolyKinds #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Obtains a 'Schema' from a set of Haskell types.
--
--
-- Unfortunately, GHC does not allow type families
-- to appear in instances, so you cannot use the
-- resulting type directly. Instead, evaluate it
@ -14,13 +16,13 @@ module Mu.Schema.Conversion.TypesToSchema (
, FromTypes, FromType(..)
) where
import Data.Kind
import Data.Map as M
import Data.SOP
import GHC.Generics
import GHC.TypeLits
import Data.Kind
import Data.Map as M
import Data.SOP
import GHC.Generics
import GHC.TypeLits
import Mu.Schema.Definition
import Mu.Schema.Definition
type FromTypes = [FromType Symbol Symbol]
data FromType tn fn
@ -101,4 +103,4 @@ type family ChoicesFromType (all :: [FromType tn fn]) (mp :: Mappings Symbol fn)
ChoicesFromType all mp (C1 ('MetaCons cname p s) f)
= TypeError ('Text "constructor " ':<>: 'ShowType cname ':<>: 'Text "has fields and cannot be turned into an enumeration schema")
ChoicesFromType all mp v
= TypeError ('Text "unsupported conversion from " ':<>: 'ShowType v ':<>: 'Text " to enumeration schema")
= TypeError ('Text "unsupported conversion from " ':<>: 'ShowType v ':<>: 'Text " to enumeration schema")

View File

@ -1,14 +1,18 @@
{-# language PolyKinds, DataKinds,
TypeFamilies, TypeOperators,
UndecidableInstances, FlexibleInstances,
ScopedTypeVariables, TypeApplications #-}
{-# language DataKinds #-}
{-# language FlexibleInstances #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Schema definition
module Mu.Schema.Definition where
import Data.Kind
import Data.Proxy
import Data.Typeable
import GHC.TypeLits
import Data.Kind
import Data.Proxy
import Data.Typeable
import GHC.TypeLits
-- | A set of type definitions,
-- where the names of types and fields are
@ -40,7 +44,7 @@ type SchemaB builtin typeName fieldName
= [TypeDefB builtin typeName fieldName]
-- | Libraries can define custom annotations
-- to indicate additional information.
-- to indicate additional information.
type Annotation = Type
-- | Defines a type in a schema.
@ -167,4 +171,4 @@ instance ReflectFieldTypes '[] where
reflectFieldTypes _ = []
instance (ReflectFieldType t, ReflectFieldTypes ts)
=> ReflectFieldTypes (t ': ts) where
reflectFieldTypes _ = reflectFieldType (Proxy @t) : reflectFieldTypes (Proxy @ts)
reflectFieldTypes _ = reflectFieldType (Proxy @t) : reflectFieldTypes (Proxy @ts)

View File

@ -1,23 +1,30 @@
{-# language PolyKinds, DataKinds, GADTs,
TypeFamilies, TypeOperators,
MultiParamTypeClasses, FlexibleInstances,
TypeApplications,
DeriveGeneric, DerivingVia, DeriveAnyClass,
TemplateHaskell, QuasiQuotes #-}
{-# language DataKinds #-}
{-# language DeriveAnyClass #-}
{-# language DeriveGeneric #-}
{-# language DerivingVia #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language QuasiQuotes #-}
{-# language TemplateHaskell #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
-- | Look at my source code!
module Mu.Schema.Examples where
import qualified Data.Aeson as J
import qualified Data.Text as T
import GHC.Generics
import qualified Data.Aeson as J
import qualified Data.Text as T
import GHC.Generics
import Mu.Schema
import Mu.Schema.Conversion.SchemaToTypes
import Mu.Adapter.Json ()
import Mu.Adapter.Json ()
import Mu.Schema
import Mu.Schema.Conversion.SchemaToTypes
data Person
= Person { firstName :: T.Text
, lastName :: T.Text
, lastName :: T.Text
, age :: Maybe Int
, gender :: Maybe Gender
, address :: Address }
@ -90,4 +97,4 @@ type ExampleSchema2
]
type ExampleRegistry
= '[ 2 ':-> ExampleSchema2, 1 ':-> ExampleSchema]
= '[ 2 ':-> ExampleSchema2, 1 ':-> ExampleSchema]

View File

@ -1,19 +1,24 @@
{-# language PolyKinds, DataKinds, GADTs,
TypeFamilies, TypeOperators,
FlexibleInstances, FlexibleContexts,
TypeApplications, ScopedTypeVariables,
UndecidableInstances #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Interpretation of schemas
module Mu.Schema.Interpretation (
Term(..), Field(..), FieldValue(..)
, NS(..), NP(..), Proxy(..)
) where
import Data.Map
import Data.Proxy
import Data.SOP
import Mu.Schema.Definition
import Data.Map
import Data.Proxy
import Data.SOP
import Mu.Schema.Definition
-- | Interpretation of a type in a schema.
data Term (sch :: Schema typeName fieldName) (t :: TypeDef typeName fieldName) where

View File

@ -1,14 +1,17 @@
{-# language PolyKinds, DataKinds, GADTs,
TypeOperators,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
UndecidableInstances,
StandaloneDeriving #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language StandaloneDeriving #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
module Mu.Schema.Interpretation.Anonymous where
import Data.SOP
import Data.SOP
import Mu.Schema
import Mu.Schema
data V0 sch sty where
V0 :: (sch :/: sty ~ 'DRecord nm anns '[])
@ -67,4 +70,4 @@ instance (sch :/: sty ~ 'DRecord nm anns '[ 'FieldDef f fanns ('TPrimitive a)
, 'FieldDef g ganns ('TPrimitive b) ])
=> HasSchema sch sty (V2 sch sty) where
toSchema (V2 x y) = TRecord (Field (FPrimitive x) :* Field (FPrimitive y) :* Nil)
fromSchema (TRecord (Field (FPrimitive x) :* Field (FPrimitive y) :* Nil)) = V2 x y
fromSchema (TRecord (Field (FPrimitive x) :* Field (FPrimitive y) :* Nil)) = V2 x y

View File

@ -1,9 +1,15 @@
{-# language PolyKinds, DataKinds, GADTs,
ScopedTypeVariables,
TypeApplications, TypeOperators,
FlexibleContexts, MultiParamTypeClasses,
AllowAmbiguousTypes, StandaloneDeriving,
FlexibleInstances, UndecidableInstances #-}
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language StandaloneDeriving #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
module Mu.Schema.Interpretation.Schemaless (
-- * Terms without an associated schema
Term(..), Field(..), FieldValue(..)
@ -13,16 +19,16 @@ module Mu.Schema.Interpretation.Schemaless (
, ToSchemalessTerm(..), ToSchemalessValue(..)
) where
import Control.Applicative ((<|>))
import Data.List (find)
import qualified Data.Map as M
import Data.Proxy
import Data.SOP
import qualified Data.Text as T
import Data.Typeable
import Control.Applicative ((<|>))
import Data.List (find)
import qualified Data.Map as M
import Data.Proxy
import Data.SOP
import qualified Data.Text as T
import Data.Typeable
import Mu.Schema.Class
import Mu.Schema.Definition
import Mu.Schema.Class
import Mu.Schema.Definition
import qualified Mu.Schema.Interpretation as S
-- | Interpretation of a type in a schema.
@ -77,7 +83,7 @@ class CheckSchemaUnion (s :: Schema tn fn) (ts :: [FieldType tn]) where
instance CheckSchemaFields s fields => CheckSchema s ('DRecord nm anns fields) where
checkSchema' (TRecord fields) = S.TRecord <$> checkSchemaFields fields
checkSchema' _ = Nothing
checkSchema' _ = Nothing
instance CheckSchemaFields s '[] where
checkSchemaFields _ = pure Nil
instance (KnownName nm, CheckSchemaValue s ty, CheckSchemaFields s rest)
@ -96,7 +102,7 @@ instance CheckSchemaEnum choices => CheckSchema s ('DEnum nm anns choices) where
(Just Refl, _, _) -> S.TEnum <$> checkSchemaEnumInt n
(_, Just Refl, _) -> S.TEnum <$> checkSchemaEnumText n
(_, _, Just Refl) -> S.TEnum <$> checkSchemaEnumText (T.pack n)
_ -> Nothing
_ -> Nothing
checkSchema' _ = Nothing
instance CheckSchemaEnum '[] where
checkSchemaEnumInt _ = Nothing
@ -125,7 +131,7 @@ instance Typeable t => CheckSchemaValue s ('TPrimitive t) where
instance (CheckSchema s (s :/: t))
=> CheckSchemaValue s ('TSchematic t) where
checkSchemaValue (FSchematic t) = S.FSchematic <$> checkSchema' t
checkSchemaValue _ = Nothing
checkSchemaValue _ = Nothing
instance CheckSchemaValue s t => CheckSchemaValue s ('TOption t) where
checkSchemaValue (FOption x) = S.FOption <$> traverse checkSchemaValue x
checkSchemaValue _ = Nothing
@ -184,4 +190,4 @@ instance Ord FieldValue where
FMap _ <= FOption _ = False
FMap _ <= FList _ = False
FMap x <= FMap y = x <= y
-- FMap _ <= _ = True
-- FMap _ <= _ = True

View File

@ -1,8 +1,14 @@
{-# language PolyKinds, DataKinds, TypeFamilies,
ScopedTypeVariables, MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
TypeOperators, UndecidableInstances,
TypeApplications, AllowAmbiguousTypes #-}
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
module Mu.Schema.Registry (
-- * Registry of schemas
Registry, fromRegistry
@ -10,18 +16,18 @@ module Mu.Schema.Registry (
, SLess.Term(..), SLess.Field(..), SLess.FieldValue(..)
) where
import Data.Proxy
import Data.Kind
import Control.Applicative
import GHC.TypeLits
import Control.Applicative
import Data.Kind
import Data.Proxy
import GHC.TypeLits
import Mu.Schema.Definition
import Mu.Schema.Class
import Mu.Schema.Class
import Mu.Schema.Definition
import qualified Mu.Schema.Interpretation.Schemaless as SLess
type Registry = Mappings Nat Schema'
fromRegistry :: forall r t.
fromRegistry :: forall r t.
FromRegistry r t
=> SLess.Term -> Maybe t
fromRegistry = fromRegistry' (Proxy @r)
@ -33,4 +39,4 @@ instance FromRegistry '[] t where
fromRegistry' _ _ = Nothing
instance (HasSchema s sty t, SLess.CheckSchema s (s :/: sty), FromRegistry ms t)
=> FromRegistry ( (n ':-> s) ': ms) t where
fromRegistry' _ t = SLess.fromSchemalessTerm @s t <|> fromRegistry' (Proxy @ms) t
fromRegistry' _ t = SLess.fromSchemalessTerm @s t <|> fromRegistry' (Proxy @ms) t

View File

@ -1,10 +1,10 @@
{-# language OverloadedStrings #-}
module Main where
import Mu.Server.GRpc
import Mu.Rpc.Examples
import Mu.Rpc.Examples
import Mu.Server.GRpc
main :: IO ()
main = do
main = do
putStrLn "running quickstart application"
runGRpcApp 8080 quickstartServer
runGRpcApp 8080 quickstartServer

View File

@ -1,70 +1,72 @@
{-# language DataKinds, ScopedTypeVariables,
TypeOperators, OverloadedStrings,
FlexibleContexts, AllowAmbiguousTypes,
DeriveGeneric, TypeApplications #-}
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language DeriveGeneric #-}
{-# language FlexibleContexts #-}
{-# language OverloadedStrings #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
module Main where
import Data.Conduit
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import qualified Data.Text as T
import GHC.Generics (Generic)
import System.Environment
import qualified Data.Text as T
import GHC.Generics (Generic)
import System.Environment
import Mu.GRpc.Client.Record
import Mu.GRpc.Client.Record
import Definition
import Definition
data HealthCall
= HealthCall
{ setStatus :: HealthStatusMsg -> IO (GRpcReply ())
, check :: HealthCheckMsg -> IO (GRpcReply ServerStatusMsg)
, clearStatus :: HealthCheckMsg -> IO (GRpcReply ())
, checkAll :: IO (GRpcReply AllStatusMsg)
, cleanAll :: IO (GRpcReply ())
, watch :: HealthCheckMsg -> IO (ConduitT () (GRpcReply ServerStatusMsg) IO ()) }
deriving (Generic)
data HealthCall = HealthCall
{ setStatus :: HealthStatusMsg -> IO (GRpcReply ())
, check :: HealthCheckMsg -> IO (GRpcReply ServerStatusMsg)
, clearStatus :: HealthCheckMsg -> IO (GRpcReply ())
, checkAll :: IO (GRpcReply AllStatusMsg)
, cleanAll :: IO (GRpcReply ())
, watch :: HealthCheckMsg -> IO (ConduitT () (GRpcReply ServerStatusMsg) IO ())
} deriving (Generic)
buildHealthCall :: GrpcClient -> HealthCall
buildHealthCall = buildService @HealthCheckService @""
main :: IO ()
main
= do -- Setup the client
let config = grpcClientConfigSimple "127.0.0.1" 8080 False
Right grpcClient <- setupGrpcClient' config
let client = buildHealthCall grpcClient
-- Execute command
args <- getArgs
case args of
["watch" , who] -> watching client who
["simple", who] -> simple client who
["update", who] -> update client who "SERVING"
["update", who, newstatus] -> update client who newstatus
_ -> putStrLn "unknown command"
main = do -- Setup the client
let config = grpcClientConfigSimple "127.0.0.1" 8080 False
Right grpcClient <- setupGrpcClient' config
let client = buildHealthCall grpcClient
-- Execute command
args <- getArgs
case args of
["watch" , who] -> watching client who
["simple", who] -> simple client who
["update", who] -> update client who "SERVING"
["update", who, newstatus] -> update client who newstatus
_ -> putStrLn "unknown command"
simple :: HealthCall -> String -> IO ()
simple client who
= do let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Is there some server named " <> who <> "?")
rknown <- check client hcm
putStrLn ("UNARY: Actually the status is " <> show rknown)
update client who "SERVING"
r <- clearStatus client hcm
putStrLn ("UNARY: Was clearing successful? " <> show r)
runknown <- check client hcm
putStrLn ("UNARY: Current status of " <> who <> ": " <> show runknown)
simple client who = do
let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Is there some server named " <> who <> "?")
rknown <- check client hcm
putStrLn ("UNARY: Actually the status is " <> show rknown)
update client who "SERVING"
r <- clearStatus client hcm
putStrLn ("UNARY: Was clearing successful? " <> show r)
runknown <- check client hcm
putStrLn ("UNARY: Current status of " <> who <> ": " <> show runknown)
update :: HealthCall -> String -> String -> IO ()
update client who newstatus
= do let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Setting " <> who <> " service to " <> newstatus)
r <- setStatus client (HealthStatusMsg hcm (ServerStatusMsg (T.pack newstatus)))
putStrLn ("UNARY: Was setting successful? " <> show r)
rstatus <- check client hcm
putStrLn ("UNARY: Checked the status of " <> who <> ". Obtained: " <> show rstatus)
update client who newstatus = do
let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Setting " <> who <> " service to " <> newstatus)
r <- setStatus client (HealthStatusMsg hcm (ServerStatusMsg (T.pack newstatus)))
putStrLn ("UNARY: Was setting successful? " <> show r)
rstatus <- check client hcm
putStrLn ("UNARY: Checked the status of " <> who <> ". Obtained: " <> show rstatus)
watching :: HealthCall -> String -> IO ()
watching client who
= do let hcm = HealthCheckMsg (T.pack who)
stream <- watch client hcm
runConduit $ stream .| C.mapM_ print
watching client who = do
let hcm = HealthCheckMsg (T.pack who)
stream <- watch client hcm
runConduit $ stream .| C.mapM_ print

View File

@ -1,59 +1,61 @@
{-# language DataKinds, ScopedTypeVariables,
TypeApplications, TypeOperators,
FlexibleContexts, AllowAmbiguousTypes,
OverloadedStrings #-}
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language OverloadedStrings #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
module Main where
import Data.Conduit
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import qualified Data.Text as T
import System.Environment
import qualified Data.Text as T
import System.Environment
import Mu.GRpc.Client.TyApps
import Mu.GRpc.Client.TyApps
import Definition
import Definition
main :: IO ()
main
= do -- Setup the client
let config = grpcClientConfigSimple "127.0.0.1" 8080 False
Right client <- setupGrpcClient' config
-- Execute command
args <- getArgs
case args of
["watch" , who] -> watching client who
["simple", who] -> simple client who
["update", who] -> update client who "SERVING"
["update", who, newstatus] -> update client who newstatus
_ -> putStrLn "unknown command"
main = do -- Setup the client
let config = grpcClientConfigSimple "127.0.0.1" 8080 False
Right client <- setupGrpcClient' config
-- Execute command
args <- getArgs
case args of
["watch" , who] -> watching client who
["simple", who] -> simple client who
["update", who] -> update client who "SERVING"
["update", who, newstatus] -> update client who newstatus
_ -> putStrLn "unknown command"
simple :: GrpcClient -> String -> IO ()
simple client who
= do let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Is there some server named " <> who <> "?")
rknown :: GRpcReply ServerStatusMsg
<- gRpcCall @HealthCheckService @"check" client hcm
putStrLn ("UNARY: Actually the status is " <> show rknown)
update client who "SERVING"
r <- gRpcCall @HealthCheckService @"clearStatus" client hcm
putStrLn ("UNARY: Was clearing successful? " <> show r)
runknown :: GRpcReply ServerStatusMsg
<- gRpcCall @HealthCheckService @"check" client hcm
putStrLn ("UNARY: Current status of " <> who <> ": " <> show runknown)
simple client who = do
let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Is there some server named " <> who <> "?")
rknown :: GRpcReply ServerStatusMsg
<- gRpcCall @HealthCheckService @"check" client hcm
putStrLn ("UNARY: Actually the status is " <> show rknown)
update client who "SERVING"
r <- gRpcCall @HealthCheckService @"clearStatus" client hcm
putStrLn ("UNARY: Was clearing successful? " <> show r)
runknown :: GRpcReply ServerStatusMsg
<- gRpcCall @HealthCheckService @"check" client hcm
putStrLn ("UNARY: Current status of " <> who <> ": " <> show runknown)
update :: GrpcClient -> String -> String -> IO ()
update client who newstatus
= do let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Setting " <> who <> " service to " <> newstatus)
r <- gRpcCall @HealthCheckService @"setStatus" client
(HealthStatusMsg hcm (ServerStatusMsg (T.pack newstatus)))
putStrLn ("UNARY: Was setting successful? " <> show r)
rstatus :: GRpcReply ServerStatusMsg
<- gRpcCall @HealthCheckService @"check" client hcm
putStrLn ("UNARY: Checked the status of " <> who <> ". Obtained: " <> show rstatus)
update client who newstatus = do
let hcm = HealthCheckMsg (T.pack who)
putStrLn ("UNARY: Setting " <> who <> " service to " <> newstatus)
r <- gRpcCall @HealthCheckService @"setStatus" client
(HealthStatusMsg hcm (ServerStatusMsg (T.pack newstatus)))
putStrLn ("UNARY: Was setting successful? " <> show r)
rstatus :: GRpcReply ServerStatusMsg
<- gRpcCall @HealthCheckService @"check" client hcm
putStrLn ("UNARY: Checked the status of " <> who <> ". Obtained: " <> show rstatus)
watching :: GrpcClient -> String -> IO ()
watching client who
= do let hcm = HealthCheckMsg (T.pack who)
replies <- gRpcCall @HealthCheckService @"watch" client hcm
runConduit $ replies .| C.mapM_ (print :: GRpcReply ServerStatusMsg -> IO ())
watching client who = do
let hcm = HealthCheckMsg (T.pack who)
replies <- gRpcCall @HealthCheckService @"watch" client hcm
runConduit $ replies .| C.mapM_ (print :: GRpcReply ServerStatusMsg -> IO ())

View File

@ -1,16 +1,22 @@
{-# language PolyKinds, DataKinds, TypeOperators,
MultiParamTypeClasses, TypeFamilies,
FlexibleInstances, FlexibleContexts,
DeriveGeneric, DeriveAnyClass,
DuplicateRecordFields, OverloadedLabels,
TemplateHaskell #-}
{-# language DataKinds #-}
{-# language DeriveAnyClass #-}
{-# language DeriveGeneric #-}
{-# language DuplicateRecordFields #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language OverloadedLabels #-}
{-# language PolyKinds #-}
{-# language TemplateHaskell #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
module Definition where
import GHC.Generics
import Data.Text as T
import Data.Text as T
import GHC.Generics
import Mu.Schema
import Mu.Quasi.GRpc
import Mu.Quasi.GRpc
import Mu.Schema
$(grpc "HealthCheckSchema" id "healthcheck.proto")
@ -53,4 +59,4 @@ type HealthCheckService
, 'Method "cleanAll" '[] '[ ] 'RetNothing
, 'Method "watch" '[] '[ 'ArgSingle (HS "HealthCheck") ] ('RetStream (HS "ServerStatus"))
]
-}
-}

View File

@ -1,22 +1,23 @@
{-# language OverloadedStrings, PartialTypeSignatures #-}
{-# language OverloadedStrings #-}
{-# language PartialTypeSignatures #-}
module Main where
import Control.Concurrent.STM
import Data.Conduit
import Control.Concurrent.STM
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import Data.Conduit.TMChan
import Data.Maybe
import qualified Data.Text as T
import DeferredFolds.UnfoldlM
import qualified StmContainers.Map as M
import Data.Conduit.TMChan
import Data.Maybe
import qualified Data.Text as T
import DeferredFolds.UnfoldlM
import qualified StmContainers.Map as M
import Mu.Server
import Mu.GRpc.Server
import Mu.GRpc.Server
import Mu.Server
import Definition
import Definition
main :: IO ()
main = do
main = do
m <- M.newIO
upd <- newTBMChanIO 100
putStrLn "running health check application"
@ -29,48 +30,45 @@ type StatusMap = M.Map T.Text T.Text
type StatusUpdates = TBMChan HealthStatusMsg
server :: StatusMap -> StatusUpdates -> ServerIO HealthCheckService _
server m upd
= Server (setStatus_ m upd :<|>:
checkH_ m :<|>:
clearStatus_ m :<|>:
checkAll_ m :<|>:
cleanAll_ m :<|>:
watch_ upd :<|>: H0)
server m upd = Server (setStatus_ m upd :<|>: checkH_ m :<|>: clearStatus_ m :<|>:
checkAll_ m :<|>: cleanAll_ m :<|>: watch_ upd :<|>: H0)
setStatus_ :: StatusMap -> StatusUpdates -> HealthStatusMsg -> IO ()
setStatus_ m upd s@(HealthStatusMsg (HealthCheckMsg nm) (ServerStatusMsg ss))
= do putStr "setStatus: " >> print (nm, ss)
atomically $ do M.insert ss nm m
writeTBMChan upd s
setStatus_ m upd s@(HealthStatusMsg (HealthCheckMsg nm) (ServerStatusMsg ss)) = do
putStr "setStatus: " >> print (nm, ss)
atomically $ do
M.insert ss nm m
writeTBMChan upd s
checkH_ :: StatusMap -> HealthCheckMsg -> IO ServerStatusMsg
checkH_ m (HealthCheckMsg nm)
= do putStr "check: " >> print nm
ss <- atomically $ M.lookup nm m
return $ ServerStatusMsg (fromMaybe "UNKNOWN" ss)
checkH_ m (HealthCheckMsg nm) = do
putStr "check: " >> print nm
ss <- atomically $ M.lookup nm m
return $ ServerStatusMsg (fromMaybe "UNKNOWN" ss)
clearStatus_ :: StatusMap -> HealthCheckMsg -> IO ()
clearStatus_ m (HealthCheckMsg nm)
= do putStr "clearStatus: " >> print nm
atomically $ M.delete nm m
clearStatus_ m (HealthCheckMsg nm) = do
putStr "clearStatus: " >> print nm
atomically $ M.delete nm m
checkAll_ :: StatusMap -> IO AllStatusMsg
checkAll_ m
= do putStrLn "checkAll"
AllStatusMsg <$> atomically (consumeValues kvToStatus (M.unfoldlM m))
where consumeValues :: Monad m => (k -> v -> a) -> UnfoldlM m (k,v) -> m [a]
consumeValues f = foldlM' (\xs (x,y) -> pure (f x y:xs)) []
kvToStatus k v = HealthStatusMsg (HealthCheckMsg k) (ServerStatusMsg v)
checkAll_ m = do
putStrLn "checkAll"
AllStatusMsg <$> atomically (consumeValues kvToStatus (M.unfoldlM m))
where
consumeValues :: Monad m => (k -> v -> a) -> UnfoldlM m (k,v) -> m [a]
consumeValues f = foldlM' (\xs (x,y) -> pure (f x y:xs)) []
kvToStatus k v = HealthStatusMsg (HealthCheckMsg k) (ServerStatusMsg v)
cleanAll_ :: StatusMap -> IO ()
cleanAll_ m
= do putStrLn "cleanAll"
atomically $ M.reset m
cleanAll_ m = do
putStrLn "cleanAll"
atomically $ M.reset m
watch_ :: StatusUpdates -> HealthCheckMsg -> ConduitT ServerStatusMsg Void IO () -> IO ()
watch_ upd hcm@(HealthCheckMsg nm) sink
= do putStr "watch: " >> print nm
runConduit $ sourceTBMChan upd
.| C.filter (\(HealthStatusMsg c _) -> hcm == c)
.| C.map (\(HealthStatusMsg _ s) -> s)
.| sink
watch_ upd hcm@(HealthCheckMsg nm) sink = do
putStr "watch: " >> print nm
runConduit $ sourceTBMChan upd
.| C.filter (\(HealthStatusMsg c _) -> hcm == c)
.| C.map (\(HealthStatusMsg _ s) -> s)
.| sink

View File

@ -1,17 +1,23 @@
{-# language PolyKinds, DataKinds, TypeOperators,
MultiParamTypeClasses, TypeFamilies,
FlexibleInstances, FlexibleContexts,
DeriveGeneric, DeriveAnyClass,
DuplicateRecordFields, TemplateHaskell #-}
{-# language DataKinds #-}
{-# language DeriveAnyClass #-}
{-# language DeriveGeneric #-}
{-# language DuplicateRecordFields #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language TemplateHaskell #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
module Definition where
import GHC.Generics
import Data.Hashable
import Data.Int
import Data.Text as T
import Data.Hashable
import Data.Int
import Data.Text as T
import GHC.Generics
import Mu.Schema
import Mu.Quasi.GRpc
import Mu.Quasi.GRpc
import Mu.Schema
$(grpc "RouteGuideSchema" id "routeguide.proto")
@ -66,4 +72,4 @@ type RouteGuideSchema
, 'FieldDef "distance" '[ProtoBufId 3] ('TPrimitive Int32)
, 'FieldDef "elapsed_time" '[ProtoBufId 4] ('TPrimitive Int32) ]
]
-}
-}

View File

@ -1,28 +1,30 @@
{-# language OverloadedStrings, PartialTypeSignatures,
DuplicateRecordFields, ScopedTypeVariables #-}
{-# language DuplicateRecordFields #-}
{-# language OverloadedStrings #-}
{-# language PartialTypeSignatures #-}
{-# language ScopedTypeVariables #-}
module Main where
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Concurrent.STM.TBMChan
import Control.Monad.IO.Class (liftIO)
import Data.Angle
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import Data.Conduit.List (sourceList)
import Data.Function ((&))
import Data.Int
import Data.List (find)
import Data.Maybe
import Data.Time.Clock
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Concurrent.STM.TBMChan
import Control.Monad.IO.Class (liftIO)
import Data.Angle
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import Data.Conduit.List (sourceList)
import Data.Function ((&))
import Data.Int
import Data.List (find)
import Data.Maybe
import Data.Time.Clock
import Mu.Server
import Mu.GRpc.Server
import Mu.GRpc.Server
import Mu.Server
import Definition
import Definition
main :: IO ()
main = do
main = do
putStrLn "running route guide application"
let features = []
routeNotes <- newTBMChanIO 100
@ -34,16 +36,14 @@ main = do
type Features = [Feature]
findFeatureIn :: Features -> Point -> Maybe Feature
findFeatureIn features p
= find (\(Feature _ loc) -> loc == p) features
findFeatureIn features p = find (\(Feature _ loc) -> loc == p) features
withinBounds :: Rectangle -> Point -> Bool
withinBounds (Rectangle (Point lox loy) (Point hix hiy)) (Point x y)
= x >= lox && x <= hix && y >= loy && y <= hiy
featuresWithinBounds :: Features -> Rectangle -> Features
featuresWithinBounds fs rect
= filter (\(Feature _ loc) -> withinBounds rect loc) fs
featuresWithinBounds fs rect = filter (\(Feature _ loc) -> withinBounds rect loc) fs
calcDistance :: Point -> Point -> Int32
calcDistance (Point lat1 lon1) (Point lat2 lon2)
@ -54,7 +54,7 @@ calcDistance (Point lat1 lon1) (Point lat2 lon2)
Radians (deltaLambda :: Double) = radians (Degrees (int32ToDouble $ lon2 - lon1))
a = sin (deltaPhi / 2) * sin (deltaPhi / 2)
+ cos phi1 * cos phi2 * sin (deltaLambda / 2) * sin (deltaLambda / 2)
c = 2 * atan2 (sqrt a) (sqrt (1 - a))
c = 2 * atan2 (sqrt a) (sqrt (1 - a))
in fromInteger $ r * ceiling c
where int32ToDouble :: Int32 -> Double
int32ToDouble = fromInteger . toInteger
@ -63,49 +63,48 @@ calcDistance (Point lat1 lon1) (Point lat2 lon2)
-- https://github.com/higherkindness/mu/blob/master/modules/examples/routeguide/server/src/main/scala/handlers/RouteGuideServiceHandler.scala
server :: Features -> TBMChan RouteNote -> ServerIO RouteGuideService _
server f m
= Server (getFeature f :<|>: listFeatures f
:<|>: recordRoute f :<|>: routeChat m :<|>: H0)
server f m = Server
(getFeature f :<|>: listFeatures f :<|>: recordRoute f :<|>: routeChat m :<|>: H0)
getFeature :: Features -> Point -> IO Feature
getFeature fs p
= return $ fromMaybe (Feature "" (Point 0 0)) (findFeatureIn fs p)
getFeature fs p = return $ fromMaybe (Feature "" (Point 0 0)) (findFeatureIn fs p)
listFeatures :: Features -> Rectangle -> ConduitT Feature Void IO () -> IO ()
listFeatures fs rect result
= runConduit $ sourceList (featuresWithinBounds fs rect) .| result
listFeatures fs rect result = runConduit $ sourceList (featuresWithinBounds fs rect) .| result
recordRoute :: Features -> ConduitT () Point IO () -> IO RouteSummary
recordRoute fs ps
= do initialTime <- getCurrentTime
(rs, _, _) <- runConduit $ ps .| C.foldM step (RouteSummary 0 0 0 0, Nothing, initialTime)
return rs
where step :: (RouteSummary, Maybe Point, UTCTime) -> Point -> IO (RouteSummary, Maybe Point, UTCTime)
step (summary, previous, startTime) point
= do currentTime <- getCurrentTime
let feature = findFeatureIn fs point
new_distance = fmap (`calcDistance` point) previous & fromMaybe 0
new_elapsed = diffUTCTime currentTime startTime
new_summary = RouteSummary (point_count summary + 1)
(feature_count summary + if isJust feature then 1 else 0)
(distance summary + new_distance)
(floor new_elapsed)
return (new_summary, Just point, startTime)
recordRoute fs ps = do
initialTime <- getCurrentTime
(rs, _, _) <- runConduit $ ps .| C.foldM step (RouteSummary 0 0 0 0, Nothing, initialTime)
return rs
where
step :: (RouteSummary, Maybe Point, UTCTime) -> Point -> IO (RouteSummary, Maybe Point, UTCTime)
step (summary, previous, startTime) point = do
currentTime <- getCurrentTime
let feature = findFeatureIn fs point
new_distance = fmap (`calcDistance` point) previous & fromMaybe 0
new_elapsed = diffUTCTime currentTime startTime
new_summary = RouteSummary (point_count summary + 1)
(feature_count summary + if isJust feature then 1 else 0)
(distance summary + new_distance)
(floor new_elapsed)
return (new_summary, Just point, startTime)
routeChat :: TBMChan RouteNote
-> ConduitT () RouteNote IO () -> ConduitT RouteNote Void IO () -> IO ()
routeChat notesMap inS outS
= do toWatch <- newEmptyTMVarIO
-- Start two threads, one to listen, one to send
inA <- async $ runConduit $ inS .| C.mapM_ (addNoteToMap toWatch)
outA <- async $ runConduit $ readStmMap (\l1 (RouteNote _ l2)-> l1 == l2) toWatch notesMap .| outS
wait inA
wait outA
where addNoteToMap :: TMVar Point -> RouteNote -> IO ()
addNoteToMap toWatch newNote@(RouteNote _ loc)
= atomically $ do _ <- tryTakeTMVar toWatch
putTMVar toWatch loc
writeTBMChan notesMap newNote
routeChat notesMap inS outS = do
toWatch <- newEmptyTMVarIO
-- Start two threads, one to listen, one to send
inA <- async $ runConduit $ inS .| C.mapM_ (addNoteToMap toWatch)
outA <- async $ runConduit $ readStmMap (\l1 (RouteNote _ l2)-> l1 == l2) toWatch notesMap .| outS
wait inA
wait outA
where
addNoteToMap :: TMVar Point -> RouteNote -> IO ()
addNoteToMap toWatch newNote@(RouteNote _ loc) = atomically $ do
_ <- tryTakeTMVar toWatch
putTMVar toWatch loc
writeTBMChan notesMap newNote
readStmMap :: Show b => (a -> b -> Bool) -> TMVar a -> TBMChan b -> ConduitT () b IO ()
readStmMap p toWatch m = go
@ -113,6 +112,6 @@ readStmMap p toWatch m = go
go = do
v <- liftIO $ atomically $ (,) <$> readTBMChan m <*> tryReadTMVar toWatch
case v of
(Nothing, _) -> return ()
(Nothing, _) -> return ()
(Just v', Just e') | p e' v' -> liftIO (print v') >> yield v' >> go
_ -> go
_ -> go

View File

@ -1,14 +1,15 @@
{-# language DataKinds, TypeApplications #-}
{-# language DataKinds #-}
{-# language TypeApplications #-}
module Mu.GRpc.Client.Examples where
import Data.Conduit
import Data.Conduit.Combinators as C
import Data.Conduit.List (consume)
import qualified Data.Text as T
import Network.HTTP2.Client (HostName, PortNumber)
import Data.Conduit
import Data.Conduit.Combinators as C
import Data.Conduit.List (consume)
import qualified Data.Text as T
import Network.HTTP2.Client (HostName, PortNumber)
import Mu.GRpc.Client.TyApps
import Mu.Rpc.Examples
import Mu.GRpc.Client.TyApps
import Mu.Rpc.Examples
sayHello' :: HostName -> PortNumber -> T.Text -> IO (GRpcReply T.Text)
sayHello' host port req
@ -25,4 +26,4 @@ sayHi' host port n
runConduit $ cndt .| C.map (fmap (\(HelloResponse r) -> r)) .| consume
sayHi :: GrpcClient -> HiRequest -> IO (ConduitT () (GRpcReply HelloResponse) IO ())
sayHi = gRpcCall @QuickStartService @"SayHi"
sayHi = gRpcCall @QuickStartService @"SayHi"

View File

@ -1,33 +1,39 @@
{-# language PolyKinds, DataKinds, GADTs,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
ScopedTypeVariables, TypeApplications,
TypeOperators, DeriveFunctor,
AllowAmbiguousTypes,
TupleSections, UndecidableInstances #-}
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language DeriveFunctor #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Client for gRPC services defined using Mu 'Service'
module Mu.GRpc.Client.Internal where
import Control.Monad.IO.Class
import Control.Concurrent.Async
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TMChan
import Control.Concurrent.STM.TMVar
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import Data.Conduit.TMChan
import Network.HTTP2 (ErrorCode)
import Network.HTTP2.Client (ClientIO, TooMuchConcurrency, ClientError, runExceptT)
import Network.GRPC.HTTP2.Proto3Wire
import Network.GRPC.Client (RawReply, CompressMode(..), StreamDone(..),
IncomingEvent(..),OutgoingEvent(..))
import Network.GRPC.Client.Helpers
import Control.Concurrent.Async
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TMChan
import Control.Concurrent.STM.TMVar
import Control.Monad.IO.Class
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import Data.Conduit.TMChan
import Network.GRPC.Client (CompressMode (..), IncomingEvent (..),
OutgoingEvent (..), RawReply, StreamDone (..))
import Network.GRPC.Client.Helpers
import Network.GRPC.HTTP2.Proto3Wire
import Network.HTTP2 (ErrorCode)
import Network.HTTP2.Client (ClientError, ClientIO, TooMuchConcurrency,
runExceptT)
import Mu.Rpc
import Mu.Schema
import Mu.Adapter.ProtoBuf.Via
import Mu.Adapter.ProtoBuf.Via
import Mu.Rpc
import Mu.Schema
setupGrpcClient' :: GrpcClientConfig -> IO (Either ClientError GrpcClient)
setupGrpcClient' = runExceptT . setupGrpcClient
@ -49,16 +55,16 @@ data GRpcReply a
deriving (Show, Functor)
buildGRpcReply1 :: Either TooMuchConcurrency (RawReply a) -> GRpcReply a
buildGRpcReply1 (Left tmc) = GRpcTooMuchConcurrency tmc
buildGRpcReply1 (Right (Left ec)) = GRpcErrorCode ec
buildGRpcReply1 (Left tmc) = GRpcTooMuchConcurrency tmc
buildGRpcReply1 (Right (Left ec)) = GRpcErrorCode ec
buildGRpcReply1 (Right (Right (_, _, Left es))) = GRpcErrorString es
buildGRpcReply1 (Right (Right (_, _, Right r))) = GRpcOk r
buildGRpcReply2 :: Either TooMuchConcurrency (r, (RawReply a)) -> GRpcReply a
buildGRpcReply2 (Left tmc) = GRpcTooMuchConcurrency tmc
buildGRpcReply2 (Right (_, (Left ec))) = GRpcErrorCode ec
buildGRpcReply2 (Right (_, (Right (_, _, Left es)))) = GRpcErrorString es
buildGRpcReply2 (Right (_, (Right (_, _, Right r)))) = GRpcOk r
buildGRpcReply2 :: Either TooMuchConcurrency (r, RawReply a) -> GRpcReply a
buildGRpcReply2 (Left tmc) = GRpcTooMuchConcurrency tmc
buildGRpcReply2 (Right (_, Left ec)) = GRpcErrorCode ec
buildGRpcReply2 (Right (_, Right (_, _, Left es))) = GRpcErrorString es
buildGRpcReply2 (Right (_, Right (_, _, Right r))) = GRpcOk r
buildGRpcReply3 :: Either TooMuchConcurrency v -> GRpcReply ()
buildGRpcReply3 (Left tmc) = GRpcTooMuchConcurrency tmc
@ -77,7 +83,7 @@ class GRpcMethodCall method h where
instance (KnownName name, handler ~ IO (GRpcReply ()))
=> GRpcMethodCall ('Method name anns '[ ] 'RetNothing) handler where
gRpcMethodCall pkgName srvName _ client
= simplifyResponse $
= simplifyResponse $
buildGRpcReply1 <$>
rawUnary rpc client ()
where methodName = BS.pack (nameVal (Proxy @name))
@ -88,7 +94,7 @@ instance ( KnownName name, ProtoBufTypeRef rref r
=> GRpcMethodCall ('Method name anns '[ ] ('RetSingle rref)) handler where
gRpcMethodCall pkgName srvName _ client
= fmap (fmap unViaProtoBufTypeRef) $
simplifyResponse $
simplifyResponse $
buildGRpcReply1 <$>
rawUnary @_ @_ @(ViaProtoBufTypeRef rref _)rpc client ()
where methodName = BS.pack (nameVal (Proxy @name))
@ -98,7 +104,7 @@ instance ( KnownName name, ProtoBufTypeRef vref v
, handler ~ (v -> IO (GRpcReply ())) )
=> GRpcMethodCall ('Method name anns '[ 'ArgSingle vref ] 'RetNothing) handler where
gRpcMethodCall pkgName srvName _ client x
= simplifyResponse $
= simplifyResponse $
buildGRpcReply1 <$>
rawUnary @_ @(ViaProtoBufTypeRef vref _) rpc client (ViaProtoBufTypeRef x)
where methodName = BS.pack (nameVal (Proxy @name))
@ -109,7 +115,7 @@ instance ( KnownName name, ProtoBufTypeRef vref v, ProtoBufTypeRef rref r
=> GRpcMethodCall ('Method name anns '[ 'ArgSingle vref ] ('RetSingle rref)) handler where
gRpcMethodCall pkgName srvName _ client x
= fmap (fmap unViaProtoBufTypeRef) $
simplifyResponse $
simplifyResponse $
buildGRpcReply1 <$>
rawUnary @_ @(ViaProtoBufTypeRef vref _) @(ViaProtoBufTypeRef rref _)
rpc client (ViaProtoBufTypeRef x)
@ -123,9 +129,9 @@ instance ( KnownName name, ProtoBufTypeRef vref v, ProtoBufTypeRef rref r
= do -- Create a new TMChan
chan <- newTMChanIO :: IO (TMChan v)
-- Start executing the client in another thread
promise <- async $
promise <- async $
fmap (fmap unViaProtoBufTypeRef) $
simplifyResponse $
simplifyResponse $
buildGRpcReply2 <$>
rawStreamClient @_ @(ViaProtoBufTypeRef vref v) @(ViaProtoBufTypeRef rref r) rpc client ()
(\_ -> do nextVal <- liftIO $ atomically $ readTMChan chan
@ -139,7 +145,7 @@ instance ( KnownName name, ProtoBufTypeRef vref v, ProtoBufTypeRef rref r
go
Nothing -> do liftIO $ atomically $ closeTMChan chan
liftIO $ wait promise
return go
return go
where methodName = BS.pack (nameVal (Proxy @name))
rpc = RPC pkgName srvName methodName
@ -152,16 +158,16 @@ instance ( KnownName name, ProtoBufTypeRef vref v, ProtoBufTypeRef rref r
var <- newEmptyTMVarIO -- if full, this means an error
-- Start executing the client in another thread
_ <- async $ do
v <- simplifyResponse $
v <- simplifyResponse $
buildGRpcReply3 <$>
rawStreamServer @_ @(ViaProtoBufTypeRef vref v) @(ViaProtoBufTypeRef rref r)
rpc client () (ViaProtoBufTypeRef x)
(\_ _ (ViaProtoBufTypeRef newVal) -> liftIO $ atomically $
(\_ _ (ViaProtoBufTypeRef newVal) -> liftIO $ atomically $
-- on the first iteration, say that everything is OK
tryPutTMVar var (GRpcOk ()) >> writeTMChan chan newVal)
case v of
GRpcOk () -> liftIO $ atomically $ closeTMChan chan
_ -> liftIO $ atomically $ putTMVar var v
_ -> liftIO $ atomically $ putTMVar var v
-- This conduit feeds information to the other thread
let go = do firstResult <- liftIO $ atomically $ takeTMVar var
case firstResult of
@ -182,7 +188,7 @@ instance ( KnownName name, ProtoBufTypeRef vref v, ProtoBufTypeRef rref r
var <- newEmptyTMVarIO -- if full, this means an error
-- Start executing the client in another thread
_ <- async $ do
v <- simplifyResponse $
v <- simplifyResponse $
buildGRpcReply3 <$>
rawGeneralStream
@_ @(ViaProtoBufTypeRef vref v) @(ViaProtoBufTypeRef rref r)
@ -200,21 +206,21 @@ instance ( KnownName name, ProtoBufTypeRef vref v, ProtoBufTypeRef rref r
Just v -> return ((), SendMessage compress (ViaProtoBufTypeRef v)))
case v of
GRpcOk () -> liftIO $ atomically $ closeTMChan inchan
_ -> liftIO $ atomically $ putTMVar var v
_ -> liftIO $ atomically $ putTMVar var v
-- This conduit feeds information to the other thread
let go = do err <- liftIO $ atomically $ takeTMVar var
case err of
GRpcOk _ -> go2
e -> yield $ (\_ -> error "this should never happen") <$> e
e -> yield $ (\_ -> error "this should never happen") <$> e
go2 = do nextOut <- await
case nextOut of
Just v -> do liftIO $ atomically $ writeTMChan outchan v
go2
Nothing -> do r <- liftIO $ atomically $ tryReadTMChan inchan
case r of
Nothing -> return () -- both are empty, end
Just Nothing -> go2
Nothing -> return () -- both are empty, end
Just Nothing -> go2
Just (Just nextIn) -> yield nextIn >> go2
return go
where methodName = BS.pack (nameVal (Proxy @name))
rpc = RPC pkgName srvName methodName
rpc = RPC pkgName srvName methodName

View File

@ -1,9 +1,15 @@
{-# language PolyKinds, DataKinds, TypeOperators,
MultiParamTypeClasses, TypeFamilies,
FlexibleInstances, FlexibleContexts,
UndecidableInstances, TypeApplications,
ScopedTypeVariables, AllowAmbiguousTypes,
TemplateHaskell #-}
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TemplateHaskell #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Client for gRPC services defined using Mu 'Service'
-- using plain Haskell records of functions
module Mu.GRpc.Client.Record (
@ -19,21 +25,21 @@ module Mu.GRpc.Client.Record (
, generateRecordFromService
) where
import Control.Applicative
import Data.Char
import Data.Conduit (ConduitT)
import Data.Proxy
import Data.Void
import GHC.Generics hiding (NoSourceUnpackedness, NoSourceStrictness)
import GHC.TypeLits
import Language.Haskell.TH hiding (ppr)
import Language.Haskell.TH.Datatype
import Control.Applicative
import Data.Char
import Data.Conduit (ConduitT)
import Data.Proxy
import Data.Void
import GHC.Generics hiding (NoSourceStrictness, NoSourceUnpackedness)
import GHC.TypeLits
import Language.Haskell.TH hiding (ppr)
import Language.Haskell.TH.Datatype
import Network.GRPC.Client (CompressMode(..))
import Network.GRPC.Client.Helpers
import Network.GRPC.Client (CompressMode (..))
import Network.GRPC.Client.Helpers
import Mu.GRpc.Client.Internal
import Mu.Rpc
import Mu.GRpc.Client.Internal
import Mu.Rpc
-- | Fills in a Haskell record of functions with the corresponding
-- calls to gRPC services from a Mu 'Service' declaration.
@ -132,11 +138,11 @@ completeName :: Namer -> String -> String
completeName namer name = firstUpper (namer (firstUpper name))
firstUpper :: String -> String
firstUpper [] = error "Empty names are not allowed"
firstUpper [] = error "Empty names are not allowed"
firstUpper (x:rest) = toUpper x : rest
firstLower :: String -> String
firstLower [] = error "Empty names are not allowed"
firstLower [] = error "Empty names are not allowed"
firstLower (x:rest) = toLower x : rest
-- Parsing
@ -153,7 +159,7 @@ typeToServiceDef toplevelty
Service <$> tyString sn
<*> pure []
<*> mapM typeToMethodDef methods'
typeToMethodDef :: Type -> Maybe (Method String)
typeToMethodDef ty
= do (mn, _, args, ret) <- tyD4 'Method ty
@ -233,4 +239,4 @@ tyD4 name (SigT t _) = tyD4 name t
tyD4 name (AppT (AppT (AppT (AppT (PromotedT c) x) y) z) u)
| c == name = Just (x, y, z, u)
| otherwise = Nothing
tyD4 _ _ = Nothing
tyD4 _ _ = Nothing

View File

@ -1,7 +1,12 @@
{-# language PolyKinds, DataKinds, GADTs,
MultiParamTypeClasses, FlexibleContexts,
ScopedTypeVariables, TypeApplications,
TypeOperators, AllowAmbiguousTypes #-}
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
-- | Client for gRPC services defined using Mu 'Service'
-- using 'TypeApplications'
module Mu.GRpc.Client.TyApps (
@ -16,18 +21,18 @@ module Mu.GRpc.Client.TyApps (
, GRpcReply(..)
) where
import Network.GRPC.Client (CompressMode(..))
import Network.GRPC.Client.Helpers
import Network.GRPC.Client (CompressMode (..))
import Network.GRPC.Client.Helpers
import Mu.Rpc
import Mu.Schema
import Mu.Rpc
import Mu.Schema
import Mu.GRpc.Client.Internal
import Mu.GRpc.Client.Internal
-- | Call a method from a Mu definition.
-- This method is thought to be used with @TypeApplications@:
-- > gRpcCall @"packageName" @ServiceDeclaration @"method"
--
-- > gRpcCall @"packageName" @ServiceDeclaration @"method"
--
-- The additional arguments you must provide to 'grpcCall'
-- depend on the signature of the method itself:
-- * The resulting value is always wrapped in 'GRpcReply'.
@ -36,4 +41,4 @@ import Mu.GRpc.Client.Internal
gRpcCall :: forall s methodName h.
(GRpcServiceMethodCall s (s :-->: methodName) h)
=> GrpcClient -> h
gRpcCall = gRpcServiceMethodCall (Proxy @s) (Proxy @(s :-->: methodName))
gRpcCall = gRpcServiceMethodCall (Proxy @s) (Proxy @(s :-->: methodName))

View File

@ -1,10 +1,10 @@
{-# language OverloadedStrings #-}
module Main where
import Mu.GRpc.Server
import Mu.Rpc.Examples
import Mu.GRpc.Server
import Mu.Rpc.Examples
main :: IO ()
main = do
main = do
putStrLn "running quickstart application"
runGRpcApp 8080 quickstartServer
runGRpcApp 8080 quickstartServer

View File

@ -1,9 +1,13 @@
{-# language PolyKinds, DataKinds, GADTs,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
UndecidableInstances,
TypeApplications, TypeOperators,
ScopedTypeVariables #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
-- | Execute a Mu 'Server' using gRPC as transport layer
module Mu.GRpc.Server (
-- * Run a 'Server' directly
@ -14,29 +18,29 @@ module Mu.GRpc.Server (
, gRpcApp
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Control.Concurrent.Async
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TMVar
import Control.Monad.IO.Class
import Data.Conduit
import Data.Conduit.TMChan
import Data.Kind
import Data.Proxy
import Network.GRPC.HTTP2.Encoding (uncompressed, gzip)
import Network.GRPC.HTTP2.Proto3Wire
import Network.GRPC.Server.Wai (ServiceHandler)
import Network.GRPC.Server.Handlers
import Network.GRPC.Server.Wai as Wai
import Network.Wai (Application)
import Network.Wai.Handler.Warp (Port, Settings, run, runSettings)
import Network.Wai.Handler.WarpTLS (TLSSettings, runTLS)
import Control.Concurrent.Async
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TMVar
import Control.Monad.IO.Class
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Data.Conduit
import Data.Conduit.TMChan
import Data.Kind
import Data.Proxy
import Network.GRPC.HTTP2.Encoding (gzip, uncompressed)
import Network.GRPC.HTTP2.Proto3Wire
import Network.GRPC.Server.Handlers
import Network.GRPC.Server.Wai (ServiceHandler)
import Network.GRPC.Server.Wai as Wai
import Network.Wai (Application)
import Network.Wai.Handler.Warp (Port, Settings, run, runSettings)
import Network.Wai.Handler.WarpTLS (TLSSettings, runTLS)
import Mu.Rpc
import Mu.Server
import Mu.Schema
import Mu.Adapter.ProtoBuf.Via
import Mu.Adapter.ProtoBuf.Via
import Mu.Rpc
import Mu.Schema
import Mu.Server
-- | Run a Mu 'Server' on the given port.
runGRpcApp
@ -47,8 +51,8 @@ runGRpcApp
runGRpcApp port svr = run port (gRpcApp svr)
-- | Run a Mu 'Server' using the given 'Settings'.
--
-- Go to 'Network.Wai.Handler.Warp' to declare 'Settings'.
--
-- Go to 'Network.Wai.Handler.Warp' to declare 'Settings'.
runGRpcAppSettings
:: ( KnownName name, KnownName (FindPackageName anns)
, GRpcMethodHandlers methods handlers )
@ -88,7 +92,7 @@ gRpcServiceHandlers
gRpcServiceHandlers (Server svr) = gRpcMethodHandlers packageName serviceName svr
where packageName = BS.pack (nameVal (Proxy @(FindPackageName anns)))
serviceName = BS.pack (nameVal (Proxy @name))
class GRpcMethodHandlers (ms :: [Method mnm]) (hs :: [Type]) where
gRpcMethodHandlers :: ByteString -> ByteString
-> HandlersIO ms hs -> [ServiceHandler]
@ -204,6 +208,7 @@ instance (ProtoBufTypeRef vref v, ProtoBufTypeRef rref r)
return ((), IncomingStream cstreamHandler cstreamFinalizer, (), OutgoingStream readNext)
toTMVarConduit :: TMVar (Maybe r) -> ConduitT r Void IO ()
toTMVarConduit var = do x <- await
liftIO $ atomically $ putTMVar var x
toTMVarConduit var
toTMVarConduit var = do
x <- await
liftIO $ atomically $ putTMVar var x
toTMVarConduit var