Compare commits

..

No commits in common. "master" and "v8.0.0.rc3" have entirely different histories.

5 changed files with 119 additions and 149 deletions

View File

@ -1,11 +1,11 @@
{
"name": "bit/metagen_cs",
"description": "C# codegen support from meta descriptions",
"description": "C# codgen support from meta descriptions",
"homepage": "https://git.bit5.ru/bit/metagen_cs",
"require": {
"php": ">=7.4",
"twig/twig" : "^v3.4.3",
"bit/metagen" : "^v4.0.0"
"bit/metagen" : "^v3.0.0 || ^v4.0.0"
},
"autoload": {
"files": [

View File

@ -25,6 +25,9 @@ function supported_tokens() : array
'cs_obsolete',
//TODO:
//'i18n'
'cs_service_call_builder',
'cs_service_client',
];
}
@ -41,8 +44,6 @@ function codegen(?string $cache_dir, \mtgMetaInfo $meta, array $options = []) :
$options['meta'] = $meta;
if(!isset($options['namespace']))
$options['namespace'] = 'BitGames.Autogen';
if(!isset($options['uses']))
$options['uses'] = [];
$sliced_units = slice_units($meta->getUnits(), 50);
@ -58,11 +59,40 @@ function codegen(?string $cache_dir, \mtgMetaInfo $meta, array $options = []) :
$output['factory.cs'] = $twig->render('codegen_factory.twig', $options);
$output['services.cs'] = $twig->render('codegen_services.twig', $options);
$output['services.cs'] = codegen_services($cache_dir, filter_services_meta($meta), $options);
return $output;
}
function filter_services_meta(\mtgMetaInfo $meta) : \mtgMetaInfo
{
$res = new \mtgMetaInfo();
foreach($meta->getUnits() as $u)
{
if($u->object instanceof \mtgMetaService)
$res->addUnit($u);
}
return $res;
}
function codegen_services(?string $cache_dir, \mtgMetaInfo $meta, array $options = []) : string
{
$twig = get_twig();
if(!empty($cache_dir))
$twig->setCache($cache_dir);
$options['meta'] = $meta;
if(!isset($options['namespace']))
$options['namespace'] = 'BitGames.Autogen';
if(!isset($options['uses']))
$options['uses'] = [];
return $twig->render('codegen_services.twig', $options);
}
function get_twig(array $inc_path = []) : \Twig\Environment
{
array_unshift($inc_path, __DIR__ . "/../tpl/");

View File

@ -33,8 +33,10 @@ static public class AutogenBundle
{
switch(class_id)
{
{%- for t in meta.factorytypes ~%}
case {{t.classid}}: { return typeof({{t.fullname}}); }
{%- for u in meta.getunits ~%}
{%- if u.object is instanceof('\\mtgMetaStruct')~%}
case {{u.object.classid}}: { return typeof({{u.object.name}}); }
{%- endif ~%}
{%- endfor ~%}
default:
{

View File

@ -4,93 +4,15 @@ using System.Threading.Tasks;
using System.Collections.Generic;
using metagen;
using BitGames.Bits;
#if USE_UNITASK
using Cysharp.Threading.Tasks;
#endif
{%- for use in uses ~%}
using {{use}};
{%- endfor ~%}
{% import "service_macro.twig" as svc_macro %}
{% import "macro.twig" as cs_macro %}
{% import "service_macro.twig" as macro %}
namespace {{namespace}}
{
public interface IRpcCaller
{
#if USE_UNITASK
UniTask
#else
Task
#endif
CallRpc(metagen.IRpc rpc);
}
public interface IApiBase
{
public IRpcCaller Caller { get; }
}
public interface IRpcCallBuilder<TRpc> where TRpc : metagen.IRpc
{
public
#if USE_UNITASK
UniTask<TRpc>
#else
Task<TRpc>
#endif
Call();
}
public struct RpcCallBuilder<TRpc> : IRpcCallBuilder<TRpc> where TRpc : IRpc
{
private IRpcCaller clt;
private TRpc rpc;
public RpcCallBuilder(IRpcCaller clt, TRpc rpc)
{
this.clt = clt;
this.rpc = rpc;
}
public async
#if USE_UNITASK
UniTask<TRpc>
#else
Task<TRpc>
#endif
Call()
{
await clt.CallRpc(rpc);
return rpc;
}
}
public struct MockRpcCallBuilder<TRpc> : IRpcCallBuilder<TRpc> where TRpc : IRpc
{
public TRpc Rpc { get; }
public MockRpcCallBuilder(TRpc rpc)
{
this.Rpc = rpc;
}
#if USE_UNITASK
public UniTask<TRpc> Call()
{
return UniTask.FromResult(Rpc);
}
#else
public Task<TRpc> Call()
{
return Task.FromResult(Rpc);
}
#endif
}
{%- for unit in meta.units %}
{% if unit.object is instanceof('\\mtgMetaService') %}
@ -98,44 +20,9 @@ public struct MockRpcCallBuilder<TRpc> : IRpcCallBuilder<TRpc> where TRpc : IRpc
namespace {{unit.object.name}}
{
//constrained local interface,
//(this way we don't have to change anything in generated
//structs below)
public interface IRpc : metagen.IRpc
{}
{{macro.gen_client(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)}}
{{macro.gen_server(unit.object)}}
}

View File

@ -1,33 +1,87 @@
{%- macro gen_client(obj) ~%}
public interface IApi : IApiBase
{% 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 static IRpc CreateRPC(int code)
{
switch(code)
{
{%- for rpc in obj.rpcs ~%}
public IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc);
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<TRpc> where TRpc : IRpc
{
private IRpcCaller clt;
private TRpc rpc;
public RpcCallBuilder(IRpcCaller clt, TRpc rpc)
{
this.clt = clt;
this.rpc = rpc;
}
public async Task<TRpc> 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);
{%- endfor ~%}
}
public class Api : IApi
{
public IRpcCaller Caller { get; }
public {{token_or(obj, 'cs_service_client', 'IRpcCaller')}} Client { get; private set; }
public Api(IRpcCaller caller)
public Api({{token_or(obj, 'cs_service_client', 'IRpcCaller')}} client)
{
this.Caller = caller;
}
public virtual IRpcCallBuilder<T> CreateCallBuilder<T>(metagen.IRpc rpc) where T : IRpc
{
return new RpcCallBuilder<T>(Caller, (T)rpc);
this.Client = client;
}
{%- for rpc in obj.rpcs ~%}
public IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc)
public {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> Begin({{rpc.name}} rpc)
{
var builder = CreateCallBuilder<{{rpc.name}}>(rpc);
return builder;
return new {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}>(Client, rpc);
}
{%- endfor ~%}
@ -37,7 +91,7 @@ public class Api : IApi
public class ApiDecoratorBase : IApi
{
private IApi orig;
public IRpcCaller Caller => orig.Caller;
public {{token_or(obj, 'cs_service_client', 'IRpcCaller')}} Client => orig.Client;
public ApiDecoratorBase(IApi orig)
{
@ -49,7 +103,7 @@ public class ApiDecoratorBase : IApi
{%- for rpc in obj.rpcs ~%}
public virtual IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc)
public virtual {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> Begin({{rpc.name}} rpc)
{
OnBefore(rpc);
return orig.Begin(rpc);
@ -61,21 +115,16 @@ public class ApiDecoratorBase : IApi
public class MockApiBase : IApi
{
public IRpcCaller Caller { get; private set; }
public {{token_or(obj, 'cs_service_client', 'IRpcCaller')}} Client { get; private set; }
static protected IRpcCallBuilder<T> Result<T>(T rpc) where T : metagen.IRpc
public MockApiBase({{token_or(obj, 'cs_service_client', 'IRpcCaller')}} client)
{
return new MockRpcCallBuilder<T>(rpc);
}
public MockApiBase(IRpcCaller caller)
{
this.Caller = caller;
this.Client = client;
}
{%- for rpc in obj.rpcs ~%}
public virtual IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc)
public virtual {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{rpc.name}}> Begin({{rpc.name}} rpc)
{
throw new System.NotImplementedException();
}
@ -101,7 +150,7 @@ static public class ApiExtensions
return __rpc;
}
static public IRpcCallBuilder<{{rpc.name}}> {{rpc.name}}(this IApi __api{%- if rpc.req.fields|length > 0 -%},{%-endif-%}
static public {{token_or(obj, 'cs_service_call_builder', 'RpcCallBuilder')}}<{{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-%}
@ -125,6 +174,8 @@ static public class ApiExtensions
{%- macro gen_server(obj) ~%}
{% import "macro.twig" as cs_macro %}
public interface IServer<TConn, TRet>
{
@ -145,7 +196,7 @@ public static class Router<TConn, TRet>
{%- endfor ~%}
}
static public TRet DispatchRequest(IServer<TConn, TRet> server, TConn conn, metagen.IRpc rpc)
static public TRet DispatchRequest(IServer<TConn, TRet> server, TConn conn, IRpc rpc)
{
switch(rpc.GetCode())
{