{-# LANGUAGE OverloadedStrings, QuasiQuotes, RecordWildCards #-}
module Language.Bond.Codegen.Cs.Grpc_cs (
grpc_cs)
where
import Data.Monoid
import qualified Data.Text.Lazy as L
import qualified Language.Bond.Codegen.Cs.Util as CS
import Language.Bond.Codegen.TypeMapping
import Language.Bond.Codegen.Util
import Language.Bond.Syntax.Types hiding (methodTypeToMaybe)
import Language.Bond.Util
import Prelude
import Text.Shakespeare.Text
data CsServerMethodParam = CsServerMethodParam { name :: L.Text, csType :: L.Text }
csServerParamDecl :: CsServerMethodParam -> L.Text
csServerParamDecl (CsServerMethodParam {..}) = [lt|#{csType} #{name}|]
data CsClientRequestParam = CsClientRequestParam
{ bareParam :: L.Text
, messageParam :: L.Text
, toMessage :: L.Text
, forwardMessageParam :: L.Text
}
grpc_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, L.Text)
grpc_cs cs _ _ declarations = ("_grpc.cs", [lt|
#{CS.disableCscWarnings}
#{CS.disableReSharperWarnings}
namespace #{csNamespace}
{
using System.Collections.Generic;
#{doubleLineSep 1 grpc declarations}
} // #{csNamespace}
|])
where
csNamespace = sepBy "." toText $ getNamespace cs
idl = MappingContext idlTypeMapping [] [] []
grpc s@Service{..} = [lt|#{CS.typeAttributes cs s}public static class #{declName}#{generics} #{genericsWhere}
{
static readonly string ServiceName = "#{getDeclTypeName idl s}";
#{doubleLineSep 2 methodDeclaration serviceMethods}
public abstract class #{declName}Base
{
#{doubleLineSep 3 serverBaseMethods serviceMethods}
}
public class #{declName}Client : global::Grpc.Core.ClientBase<#{declName}Client>
{
public #{declName}Client(global::Grpc.Core.Channel channel) : base(channel)
{
}
protected #{declName}Client() : base()
{
}
protected #{declName}Client(global::Grpc.Core.ClientBase.ClientBaseConfiguration configuration) : base(configuration)
{
}
#{doubleLineSep 3 clientMethods serviceMethods}
protected override #{declName}Client NewInstance(global::Grpc.Core.ClientBase.ClientBaseConfiguration configuration)
{
return new #{declName}Client(configuration);
}
}
public static global::Grpc.Core.ServerServiceDefinition BindService(#{declName}Base serviceImpl)
{
return global::Grpc.Core.ServerServiceDefinition.CreateBuilder()
#{newlineSep 5 serviceMethodBuilder serviceMethods}
.Build();
}
}
|]
where
methodNames = map methodName serviceMethods
uniqImplName name = uniqueName (name ++ "_impl") methodNames
csTypeOf Void = "global::Bond.Void"
csTypeOf (Unary t) = getTypeName cs t
csTypeOf (Streaming t) = getTypeName cs t
csRequestTypeName (Function {..}) = csTypeOf methodInput
csRequestTypeName (Event {..}) = csTypeOf methodInput
csResponseTypeName (Function {..}) = csTypeOf methodResult
csResponseTypeName Event{} = csTypeOf Void
messageVoid = [lt|global::Bond.Grpc.IMessage<global::Bond.Void>|]
messageOf :: Type -> L.Text
messageOf t = [lt|global::Bond.Grpc.IMessage<#{getTypeName cs t}>|]
methodTypeEnum :: Method -> L.Text
methodTypeEnum (Function {..}) = case (methodResult, methodInput) of
(Streaming _, Streaming _) -> [lt|global::Grpc.Core.MethodType.DuplexStreaming|]
(Streaming _, _) -> [lt|global::Grpc.Core.MethodType.ServerStreaming|]
(_, Streaming _) -> [lt|global::Grpc.Core.MethodType.ClientStreaming|]
_ -> [lt|global::Grpc.Core.MethodType.Unary|]
methodTypeEnum (Event {..}) = case methodInput of
Streaming _ -> error ("Unexpected event '" ++ methodName ++ "' with Streaming parameter")
_ -> [lt|global::Grpc.Core.MethodType.Unary|]
generics = angles $ sepBy ", " paramName declParams
genericsWhere = sepBy " " addWhere declParams
where
addWhere a = [lt|where #{paramName a} : class|]
methodDeclaration m = [lt|static readonly global::Grpc.Core.Method<global::Bond.Grpc.IMessage<#{csRequestTypeName m}>, global::Bond.Grpc.IMessage<#{csResponseTypeName m}>> Method_#{methodName m} = new global::Grpc.Core.Method<global::Bond.Grpc.IMessage<#{csRequestTypeName m}>, global::Bond.Grpc.IMessage<#{csResponseTypeName m}>>(
#{methodTypeEnum m},
ServiceName,
"#{methodName m}",
global::Bond.Grpc.Marshaller<#{csRequestTypeName m}>.Instance,
global::Bond.Grpc.Marshaller<#{csResponseTypeName m}>.Instance);|]
serverMethodParams :: MethodType -> MethodType -> [CsServerMethodParam]
serverMethodParams reqType respType = (forRequest reqType) ++ (forResponse respType) ++ context
where
forRequest Void = [(CsServerMethodParam [lt|request|] messageVoid)]
forRequest (Unary t) = [(CsServerMethodParam [lt|request|] [lt|#{messageOf t}|])]
forRequest (Streaming t) = [(CsServerMethodParam [lt|requests|] [lt|Grpc.Core.IAsyncStreamReader<#{messageOf t}>|])]
forResponse Void = []
forResponse (Unary _) = []
forResponse (Streaming t) = [(CsServerMethodParam [lt|responses|] [lt|Grpc.Core.IAsyncStreamWriter<#{messageOf t}>|])]
context = [(CsServerMethodParam [lt|context|] [lt|global::Grpc.Core.ServerCallContext|])]
csServerFunctionReturn :: MethodType -> L.Text
csServerFunctionReturn Void = [lt|global::System.Threading.Tasks.Task<#{messageVoid}>|]
csServerFunctionReturn (Unary t) = [lt|global::System.Threading.Tasks.Task<#{messageOf t}>|]
csServerFunctionReturn (Streaming _) = [lt|global::System.Threading.Tasks.Task|]
serverBaseMethods Function{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public abstract #{csServerFunctionReturn methodResult} #{methodName}(#{csParams});|]
where params = serverMethodParams methodInput methodResult
csParams = commaSep csServerParamDecl params
serverBaseMethods Event{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public abstract global::System.Threading.Tasks.Task #{methodName}(#{csParams});
internal global::System.Threading.Tasks.Task<#{messageVoid}> #{uniqImplName methodName}(#{csParams}) {
return global::Bond.Grpc.Internal.NothingCallHandler.Handle(#{handlerArgs});
}|]
where params = serverMethodParams methodInput Void
csParams = commaSep csServerParamDecl params
handlerArgs = commaSep id ((L.pack methodName):(map name params))
csClientFunctionReturn :: MethodType -> MethodType -> L.Text
csClientFunctionReturn (Streaming input) (Streaming result) = [lt|global::Grpc.Core.AsyncDuplexStreamingCall<#{messageOf input}, #{messageOf result}>|]
csClientFunctionReturn (Streaming input) (Unary result) = [lt|global::Grpc.Core.AsyncClientStreamingCall<#{messageOf input}, #{messageOf result}>|]
csClientFunctionReturn _ (Streaming result) = [lt|global::Grpc.Core.AsyncServerStreamingCall<#{messageOf result}>|]
csClientFunctionReturn _ (Unary result) = [lt|global::Grpc.Core.AsyncUnaryCall<#{messageOf result}>|]
csClientFunctionReturn _ Void = [lt|global::Grpc.Core.AsyncUnaryCall<#{messageVoid}>|]
csClientRequestParam :: MethodType -> CsClientRequestParam
csClientRequestParam Void = CsClientRequestParam
{ bareParam = mempty
, messageParam = [lt|#{messageVoid} request, |]
, toMessage = [lt|global::Bond.Grpc.Message.Void, |]
, forwardMessageParam = [lt|, request|] }
csClientRequestParam (Unary t) = CsClientRequestParam
{ bareParam = [lt|#{getTypeName cs t} request, |]
, messageParam = [lt|#{messageOf t} request, |]
, toMessage = [lt|global::Bond.Grpc.Message.From(request), |]
, forwardMessageParam = [lt|, request|] }
csClientRequestParam (Streaming _) = CsClientRequestParam
{ bareParam = mempty
, messageParam = mempty
, toMessage = mempty
, forwardMessageParam = mempty }
clientCallInvoker :: MethodType -> MethodType -> L.Text
clientCallInvoker (Streaming _) (Streaming _) = [lt|AsyncDuplexStreamingCall|]
clientCallInvoker (Streaming _) (Unary _) = [lt|AsyncClientStreamingCall|]
clientCallInvoker (Unary _) (Streaming _) = [lt|AsyncServerStreamingCall|]
clientCallInvoker _ _ = [lt|AsyncUnaryCall|]
clientMethods Function{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public virtual #{csClientFunctionReturn methodInput methodResult} #{methodName}Async(#{bareParam requestParam}global::Grpc.Core.Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return #{methodName}Async(#{toMessage requestParam}new global::Grpc.Core.CallOptions(headers, deadline, cancellationToken));
}
#{CS.schemaAttributes 3 methodAttributes}public virtual #{csClientFunctionReturn methodInput methodResult} #{methodName}Async(#{messageParam requestParam}global::Grpc.Core.CallOptions options)
{
return CallInvoker.#{clientCallInvoker methodInput methodResult}(Method_#{methodName}, null, options#{forwardMessageParam requestParam});
}|]
where requestParam = csClientRequestParam methodInput
clientMethods Event{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public virtual void #{methodName}Async(#{bareParam requestParam}global::Grpc.Core.Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
#{methodName}Async(#{toMessage requestParam}new global::Grpc.Core.CallOptions(headers, deadline, cancellationToken));
}
#{CS.schemaAttributes 3 methodAttributes}public virtual void #{methodName}Async(#{messageParam requestParam}global::Grpc.Core.CallOptions options)
{
global::Bond.Grpc.Internal.NothingCallInvoker.NothingCall(CallInvoker, Method_#{methodName}, null, options#{forwardMessageParam requestParam});
}|]
where requestParam = csClientRequestParam methodInput
serviceMethodBuilder Function{..} = [lt|.AddMethod(Method_#{methodName}, serviceImpl.#{methodName})|]
serviceMethodBuilder Event{..} = [lt|.AddMethod(Method_#{methodName}, serviceImpl.#{uniqImplName methodName})|]
grpc _ = mempty