From 128770343584f3cdc07a66bd0866bfc43ec52be1 Mon Sep 17 00:00:00 2001 From: Pavel Shevaev Date: Wed, 21 May 2025 18:10:14 +0300 Subject: [PATCH] Further improving services codegen --- src/codegen.inc.php | 3 -- tpl/codegen_services.twig | 90 +++++++++++++++++++++++++++++++-- tpl/service_macro.twig | 103 ++++++++++---------------------------- 3 files changed, 113 insertions(+), 83 deletions(-) diff --git a/src/codegen.inc.php b/src/codegen.inc.php index 237a992..0283fc9 100644 --- a/src/codegen.inc.php +++ b/src/codegen.inc.php @@ -25,9 +25,6 @@ function supported_tokens() : array 'cs_obsolete', //TODO: //'i18n' - - 'cs_service_call_builder', - 'cs_service_client', ]; } diff --git a/tpl/codegen_services.twig b/tpl/codegen_services.twig index f91baf2..963472b 100644 --- a/tpl/codegen_services.twig +++ b/tpl/codegen_services.twig @@ -8,11 +8,60 @@ using BitGames.Bits; using {{use}}; {%- endfor ~%} -{% import "service_macro.twig" as macro %} +{% import "service_macro.twig" as svc_macro %} +{% import "macro.twig" as cs_macro %} namespace {{namespace}} { +public interface IRpcCaller +{ + Task CallRpc(metagen.IRpc rpc); +} + +public interface IApiBase +{ + public IRpcCaller Caller { get; } +} + +public interface IRpcCallBuilder where TRpc : metagen.IRpc +{ + public Task Call(); +} + +public struct RpcCallBuilder : IRpcCallBuilder where TRpc : IRpc +{ + private IRpcCaller clt; + private TRpc rpc; + + public RpcCallBuilder(IRpcCaller clt, TRpc rpc) + { + this.clt = clt; + this.rpc = rpc; + } + + public async Task Call() + { + await clt.CallRpc(rpc); + return rpc; + } +} + +public struct MockRpcCallBuilder : IRpcCallBuilder where TRpc : IRpc +{ + public TRpc Rpc { get; } + + public MockRpcCallBuilder(TRpc rpc) + { + this.Rpc = rpc; + } + + public Task Call() + { + return Task.FromResult(Rpc); + } +} + {%- for unit in meta.units %} {% if unit.object is instanceof('\\mtgMetaService') %} @@ -20,9 +69,44 @@ namespace {{namespace}} namespace {{unit.object.name}} { -{{macro.gen_client(unit.object)}} +//constrained local interface, +//(this way we don't have to change anything in generated +//structs below) +public interface IRpc : metagen.IRpc +{} -{{macro.gen_server(unit.object)}} +{%- for rpc in unit.object.rpcs %} + {{cs_macro.decl_rpc(rpc, false)}} +{%- endfor ~%} + +{%- for utype in unit.object.usertypes %} + {%- if utype is instanceof('\\mtgMetaStruct') -%} + {{cs_macro.decl_struct(utype)}} + {% elseif utype is instanceof('\\mtgMetaEnum') %} + {{cs_macro.decl_enum(utype)}} + {% endif %} +{%- endfor ~%} + +public static class Factory +{ + public static IRpc CreateRPC(int code) + { + switch(code) + { +{%- for rpc in unit.object.rpcs ~%} + + case {{rpc.code}}: return new {{rpc.name}}(); + +{%- endfor ~%} + } + + throw new Exception("Unsupported RPC code: " + code); + } +} + +{{svc_macro.gen_client(unit.object)}} + +{{svc_macro.gen_server(unit.object)}} } diff --git a/tpl/service_macro.twig b/tpl/service_macro.twig index 22f9e5c..8faae8d 100644 --- a/tpl/service_macro.twig +++ b/tpl/service_macro.twig @@ -1,87 +1,33 @@ {%- macro gen_client(obj) ~%} -{% import "macro.twig" as cs_macro %} - -//constrained local interface -public interface IRpc : metagen.IRpc -{} - -{%- for rpc in obj.rpcs %} - {{cs_macro.decl_rpc(rpc, false)}} -{%- endfor ~%} - -{%- for utype in obj.usertypes %} - {%- if utype is instanceof('\\mtgMetaStruct') -%} - {{cs_macro.decl_struct(utype)}} - {% elseif utype is instanceof('\\mtgMetaEnum') %} - {{cs_macro.decl_enum(utype)}} - {% endif %} -{%- endfor ~%} - -public static class Factory +public interface IApi : IApiBase { - public static IRpc CreateRPC(int code) - { - switch(code) - { {%- for rpc in obj.rpcs ~%} - - case {{rpc.code}}: return new {{rpc.name}}(); - -{%- endfor ~%} - } - - throw new Exception("Unsupported RPC code: " + code); - } -} - -public interface IRpcCaller -{ - Task CallRpc(metagen.IRpc rpc); -} - -public struct RpcCallBuilder where TRpc : IRpc -{ - private IRpcCaller clt; - private TRpc rpc; - - public RpcCallBuilder(IRpcCaller clt, TRpc rpc) - { - this.clt = clt; - this.rpc = rpc; - } - - public async Task Call() - { - await clt.CallRpc(rpc); - return rpc; - } -} - -public interface IApi -{ - public {{token_or(obj, 'cs_service_client', 'IRpcCaller')}} Client { get; } - -{%- for rpc in obj.rpcs ~%} - public {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> Begin({{rpc.name}} rpc); + public IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc); {%- endfor ~%} } public class Api : IApi { - public {{token_or(obj, 'cs_service_client', 'IRpcCaller')}} Client { get; private set; } + public IRpcCaller Caller { get; } - public Api({{token_or(obj, 'cs_service_client', 'IRpcCaller')}} client) + public Api(IRpcCaller caller) { - this.Client = client; + this.Caller = caller; + } + + public virtual IRpcCallBuilder CreateCallBuilder(metagen.IRpc rpc) where T : IRpc + { + return new RpcCallBuilder(Caller, (T)rpc); } {%- for rpc in obj.rpcs ~%} - public {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> Begin({{rpc.name}} rpc) + public IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc) { - return new {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}>(Client, rpc); + var builder = CreateCallBuilder<{{rpc.name}}>(rpc); + return builder; } {%- endfor ~%} @@ -91,7 +37,7 @@ public class Api : IApi public class ApiDecoratorBase : IApi { private IApi orig; - public {{token_or(obj, 'cs_service_client', 'IRpcCaller')}} Client => orig.Client; + public IRpcCaller Caller => orig.Caller; public ApiDecoratorBase(IApi orig) { @@ -103,7 +49,7 @@ public class ApiDecoratorBase : IApi {%- for rpc in obj.rpcs ~%} - public virtual {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> Begin({{rpc.name}} rpc) + public virtual IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc) { OnBefore(rpc); return orig.Begin(rpc); @@ -115,16 +61,21 @@ public class ApiDecoratorBase : IApi public class MockApiBase : IApi { - public {{token_or(obj, 'cs_service_client', 'IRpcCaller')}} Client { get; private set; } + public IRpcCaller Caller { get; private set; } - public MockApiBase({{token_or(obj, 'cs_service_client', 'IRpcCaller')}} client) + static protected IRpcCallBuilder Result(T rpc) where T : metagen.IRpc { - this.Client = client; + return new MockRpcCallBuilder(rpc); + } + + public MockApiBase(IRpcCaller caller) + { + this.Caller = caller; } {%- for rpc in obj.rpcs ~%} - public virtual {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> Begin({{rpc.name}} rpc) + public virtual IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc) { throw new System.NotImplementedException(); } @@ -150,7 +101,7 @@ static public class ApiExtensions return __rpc; } - static public {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> {{rpc.name}}(this IApi __api{%- if rpc.req.fields|length > 0 -%},{%-endif-%} + static public IRpcCallBuilder<{{rpc.name}}> {{rpc.name}}(this IApi __api{%- if rpc.req.fields|length > 0 -%},{%-endif-%} {%- for fld in rpc.req.fields ~%} {{fld.type|cs_type}} {% if has_token(fld, 'default') -%} {{ var_reset(fld.name, fld.type, token(fld, 'default'))|trim(';', 'right') }} {%-else-%} {{fld.name}} {%-endif-%} {%- if not loop.last -%},{%-endif-%} @@ -174,8 +125,6 @@ static public class ApiExtensions {%- macro gen_server(obj) ~%} -{% import "macro.twig" as cs_macro %} - public interface IServer { @@ -196,7 +145,7 @@ public static class Router {%- endfor ~%} } - static public TRet DispatchRequest(IServer server, TConn conn, IRpc rpc) + static public TRet DispatchRequest(IServer server, TConn conn, metagen.IRpc rpc) { switch(rpc.GetCode()) {