-- Copyright (c) Microsoft. All rights reserved. -- Licensed under the MIT license. See LICENSE file in the project root for full license information. {-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-} module Language.Bond.Codegen.Cpp.Grpc_h (grpc_h) where import System.FilePath import Data.Maybe (isNothing) import Data.Monoid import Prelude import qualified Data.Text.Lazy as L import Data.Text.Lazy.Builder import Text.Shakespeare.Text import Language.Bond.Util import Language.Bond.Syntax.Types import Language.Bond.Codegen.Util import Language.Bond.Codegen.TypeMapping import qualified Language.Bond.Codegen.Cpp.Util as CPP -- | Codegen template for generating /base_name/_grpc.h containing declarations of -- of service interface and proxy. grpc_h :: Maybe String -> MappingContext -> String -> [Import] -> [Declaration] -> (String, L.Text) grpc_h export_attribute cpp file imports declarations = ("_grpc.h", [lt| #pragma once #include "#{file}_reflection.h" #include "#{file}_types.h" #{newlineSep 0 includeImport imports} #{includeBondReflection} #include #include #include #include #include #include #include #{CPP.openNamespace cpp} #{doubleLineSep 1 grpc declarations} #{CPP.closeNamespace cpp} |]) where includeImport (Import path) = [lt|#include "#{dropExtension (slashForward path)}_grpc.h"|] idl = MappingContext idlTypeMapping [] [] [] cppType = getTypeName cpp payload = maybe "void" cppType bonded mt = bonded' (payload mt) where bonded' params = [lt|::bond::bonded<#{padLeft}#{params}>|] where paramsText = toLazyText params padLeft = if L.head paramsText == ':' then [lt| |] else mempty includeBondReflection = if usesBondVoid then [lt|#include |] else mempty where usesBondVoid = any declUses declarations declUses Service {serviceMethods = methods} = any methodUses methods declUses _ = False methodUses Function {methodInput = input} = isNothing (methodTypeToMaybe input) methodUses Event {} = True grpc s@Service{..} = [lt| #{template}struct #{declName} final { struct Schema { #{export_attr}static const ::bond::Metadata metadata; #{newlineSep 2 methodMetadata serviceMethods} public: struct service { #{newlineSep 3 methodTemplate serviceMethods} }; private: typedef boost::mpl::list<> methods0; #{newlineSep 2 pushMethod indexedMethods} public: typedef #{typename}methods#{length serviceMethods}::type methods; #{constructor} }; class #{proxyName} : public ::bond::ext::grpc::detail::client { public: using ::bond::ext::grpc::detail::client::client; #{doubleLineSep 2 publicProxyMethodDecl serviceMethods} private: #{newlineSep 2 privateProxyMethodDecl serviceMethods} }; class #{serviceName} : public ::bond::ext::grpc::detail::service { public: explicit #{serviceName}(const ::bond::ext::grpc::Scheduler& scheduler) : ::bond::ext::grpc::detail::service( scheduler, { #{commaLineSep 5 serviceMethodName serviceMethods} }) {} #{newlineSep 2 serviceVirtualMethod serviceMethods} private: void start() override { _data.emplace(*this); } struct data { explicit data(#{serviceName}& s) : _s(s) {} #{serviceName}& _s; #{newlineSep 3 serviceDataMember serviceMethodsWithIndex} }; ::boost::optional _data; }; }; #{onlyTemplate $ CPP.schemaMetadata cpp s} |] where template = CPP.template s onlyTemplate x = if null declParams then mempty else x onlyNonTemplate x = if null declParams then x else mempty typename = onlyTemplate [lt|typename |] export_attr = onlyNonTemplate $ optional (\a -> [lt|#{a} |]) export_attribute methodMetadataVar m = [lt|s_#{methodName m}_metadata|] methodMetadata m = [lt|private: #{export_attr}static const ::bond::Metadata #{methodMetadataVar m};|] -- reversed list of method names zipped with indexes indexedMethods :: [(String, Int)] indexedMethods = zipWith ((,) . methodName) (reverse serviceMethods) [0..] pushMethod (method, i) = [lt|private: typedef #{typename}boost::mpl::push_front::type methods#{i + 1};|] -- constructor, generated only for service templates constructor = onlyTemplate [lt|Schema() { // Force instantiation of template statics (void)metadata; #{newlineSep 3 static serviceMethods} }|] where static m = [lt|(void)#{methodMetadataVar m};|] methodTemplate m = [lt|typedef struct : ::bond::ext::grpc::reflection::MethodTemplate<#{declName}, #{payload $ methodTypeToMaybe (methodInput m)}, #{resultType m}, &#{methodMetadataVar m}> {} #{methodName m};|] proxyName = "Client" :: String serviceName = "Service" :: String serviceMethodsWithIndex :: [(Integer,Method)] serviceMethodsWithIndex = zip [0..] serviceMethods publicProxyMethodDecl Function{methodInput = Void, ..} = [lt|void Async#{methodName}(const ::std::function)>& cb, ::std::shared_ptr<::grpc::ClientContext> context = {}) { ::bond::ext::grpc::detail::client::dispatch(_m#{methodName}, std::move(context), cb); } ::std::future<::bond::ext::grpc::unary_call_result<#{payload (methodTypeToMaybe methodResult)}>> Async#{methodName}(::std::shared_ptr<::grpc::ClientContext> context = {}) { return ::bond::ext::grpc::detail::client::dispatch<#{payload (methodTypeToMaybe methodResult)}>(_m#{methodName}, std::move(context)); }|] publicProxyMethodDecl Function{..} = [lt|void Async#{methodName}(const #{bonded (methodTypeToMaybe methodInput)}& request, const ::std::function)>& cb, ::std::shared_ptr<::grpc::ClientContext> context = {}) { ::bond::ext::grpc::detail::client::dispatch(_m#{methodName}, std::move(context), cb, request); } std::future<::bond::ext::grpc::unary_call_result<#{payload (methodTypeToMaybe methodResult)}>> Async#{methodName}(const #{bonded (methodTypeToMaybe methodInput)}& request, ::std::shared_ptr<::grpc::ClientContext> context = {}) { return ::bond::ext::grpc::detail::client::dispatch<#{payload (methodTypeToMaybe methodResult)}>(_m#{methodName}, std::move(context), request); } void Async#{methodName}(const #{payload (methodTypeToMaybe methodInput)}& request, const ::std::function)>& cb, ::std::shared_ptr<::grpc::ClientContext> context = {}) { ::bond::ext::grpc::detail::client::dispatch(_m#{methodName}, std::move(context), cb, request); } ::std::future<::bond::ext::grpc::unary_call_result<#{payload (methodTypeToMaybe methodResult)}>> Async#{methodName}(const #{payload (methodTypeToMaybe methodInput)}& request, ::std::shared_ptr<::grpc::ClientContext> context = {}) { return ::bond::ext::grpc::detail::client::dispatch<#{payload (methodTypeToMaybe methodResult)}>(_m#{methodName}, std::move(context), request); }|] publicProxyMethodDecl Event{methodInput = Void, ..} = [lt|void Async#{methodName}(::std::shared_ptr<::grpc::ClientContext> context = {}) { ::bond::ext::grpc::detail::client::dispatch(_m#{methodName}, std::move(context), {}); }|] publicProxyMethodDecl Event{..} = [lt|void Async#{methodName}(const #{bonded (methodTypeToMaybe methodInput)}& request, ::std::shared_ptr<::grpc::ClientContext> context = {}) { ::bond::ext::grpc::detail::client::dispatch(_m#{methodName}, std::move(context), {}, request); } void Async#{methodName}(const #{payload (methodTypeToMaybe methodInput)}& request, ::std::shared_ptr<::grpc::ClientContext> context = {}) { ::bond::ext::grpc::detail::client::dispatch(_m#{methodName}, std::move(context), {}, request); }|] privateProxyMethodDecl f = [lt|const ::bond::ext::grpc::detail::client::Method _m#{methodName f}{ ::bond::ext::grpc::detail::client::make_method("/#{getDeclTypeName idl s}/#{methodName f}") };|] serviceMethodName f = [lt|"/#{getDeclTypeName idl s}/#{methodName f}"|] serviceDataMember (index,f) = [lt|::bond::ext::grpc::detail::service::Method _m#{index}{ _s, #{index}, ::bond::ext::grpc::detail::service::make_callback(&#{serviceName}::#{methodName f}, _s) };|] serviceVirtualMethod f = [lt|virtual void #{methodName f}(::bond::ext::grpc::unary_call<#{payload $ methodTypeToMaybe $ methodInput f}, #{resultType f}>) = 0;|] resultType Function{..} = payload (methodTypeToMaybe methodResult) resultType Event{} = "::bond::reflection::nothing" grpc _ = mempty