Compare commits

..

1 Commits

Author SHA1 Message Date
Pavel Merzlyakov a8c8da34b2 fix order pkey fields 2023-06-08 09:56:02 +03:00
10 changed files with 199 additions and 869 deletions

View File

@ -1,30 +0,0 @@
name: Publish PHP Package
on:
push:
tags:
- 'v*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get tag name
run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: zip and send
run: |
ls -la
apt-get update -y
apt-get install -y zip
cd ../
zip -r ${{ gitea.event.repository.name }}.zip ${{ gitea.event.repository.name }} -x '*.git*'
curl -v \
--user composer-pbl:${{ secrets.COMPOSER_PSWD }} \
--upload-file ${{ gitea.event.repository.name }}.zip \
https://git.bit5.ru/api/packages/bit/composer?version=${{ env.TAG }}

View File

@ -1,30 +0,0 @@
## v7.1.0
- Using a shared helper method to streamline the generated code
## v7.0.0
- Using a dedicated wrapper for i18n strings to streamline the generated code
## v6.1.0
- Removed redundant accessor interface declarations in generated slices
## v5.0.0
- Splitting one huge codegenerated file into multiple ones
## v4.10.0
- Added @flt_i18n token to support translations
## v4.9.2
- Added @cs_obsolete token, that mark fields with [Obsolete]. @cs_obsolete:"Comment" will add summary with "Comment" to field
## v4.9.1
- Fixing nested diffable POD structs being incorrectly marked as clean in GetDiff
## v4.8.2
- Fixing thread safety possible issue when reading an array of enums
- Fixing bug when the target enum array property wouldn't be cleared before reading data
## v4.8.0
- Using C# built-in initialization routines instead of MetaIO's methods
## v4.7.0
- Array comparison generated code now takes bitfields token into account for array entries

View File

@ -2,10 +2,12 @@ This package is used for code generation of C# meta structs using Twig templates
Usage example:
$output = \metagen_cs\codegen(null, get_meta(),
[
'namespace' => 'BitGames.Autogen'
]);
foreach($output as $name => $text)
file_put_contents($name, $text);
$twig = \metagen_cs\get_twig();
file_put_contents('bundle.cs',
$twig->render("codegen_bundle.twig",
[
'namespace' => 'BitGames.Autogen',
'meta' => get_meta()
]
)
);

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"
"twig/twig" : "v3.4.3",
"bit/metagen" : "^v2.0.2"
},
"autoload": {
"files": [

View File

@ -2,68 +2,7 @@
namespace metagen_cs;
use Exception;
function supported_tokens() : array
{
return [
'POD',
'default',
'alias',
'virtual',
'bitfields',
'cloneable',
'obscured',
'diffable',
//TODO: this should be in a different package as a plugin
'table_pkey',
'cs_embed',
'cs_implements',
'cs_attributes',
'cs_accessor_interface',
'cs_propget_interface',
'cs_propset_interface',
'cs_propgetset_interface',
'cs_obsolete',
//TODO:
//'i18n'
];
}
//NOTE: returns an array of file names with their corresponding content
function codegen(?string $cache_dir, \mtgMetaInfo $meta, array $options = []) : array
{
$twig = get_twig();
if(!empty($cache_dir))
$twig->setCache($cache_dir);
$twig->addGlobal('meta', $meta);
$options['meta'] = $meta;
if(!isset($options['namespace']))
$options['namespace'] = 'BitGames.Autogen';
if(!isset($options['uses']))
$options['uses'] = [];
$sliced_units = slice_units($meta->getUnits(), 50);
$output = array();
foreach($sliced_units as $slice_idx => $slice_units)
{
$slice_options = $options;
$slice_options['slice_idx'] = $slice_idx;
$slice_options['slice_units'] = $slice_units;
$output['types_'.$slice_idx.'.cs'] = $twig->render('codegen_types_slice.twig', $slice_options);
}
$output['factory.cs'] = $twig->render('codegen_factory.twig', $options);
$output['services.cs'] = $twig->render('codegen_services.twig', $options);
return $output;
}
function get_twig(array $inc_path = []) : \Twig\Environment
function get_twig(array $inc_path = [])
{
array_unshift($inc_path, __DIR__ . "/../tpl/");
$loader = new \Twig\Loader\FilesystemLoader($inc_path);
@ -80,6 +19,31 @@ function get_twig(array $inc_path = []) : \Twig\Environment
return $twig;
}
function supported_tokens()
{
return [
'POD',
'default',
'optional',
'bitfields',
'cloneable',
'virtual',
'id',
'owner',
'pkey',
'obscured',
'diffable',
'bhl_bind',
'cs_attributes',
'cs_accessor_interface',
'cs_propget_interface',
'cs_propset_interface',
'cs_propgetset_interface',
//TODO:
//'i18n'
];
}
function _add_twig_support(\Twig\Environment $twig)
{
$twig->addTest(new \Twig\TwigTest('instanceof',
@ -97,7 +61,7 @@ function _add_twig_support(\Twig\Environment $twig)
$twig->addFunction(new \Twig\TwigFunction('has_token',
function($o, $token)
{
return (($o instanceof \mtgMetaUnit) || ($o instanceof \mtgMetaField)) && $o->hasToken($token);
return $o->hasToken($token);
}
));
$twig->addFunction(new \Twig\TwigFunction('has_token_in_parent',
@ -121,39 +85,10 @@ function _add_twig_support(\Twig\Environment $twig)
return $v;
}
));
$twig->addFunction(new \Twig\TwigFunction('field_index',
function(\mtgMetaStruct $s, string $f): int
{
$idx = get_field_index($s, $f);
if($idx == -1)
{
throw new Exception("field `$f` not found in `{$s->getName()}`");
}
return $idx;
}
));
$twig->addFunction(new \Twig\TwigFunction('find_struct',
function(string $name) use ($twig): \mtgMetaStruct
{
$meta = $twig->getGlobals()['meta'];
$unit = $meta->findUnit($name);
return $unit->object;
}
));
$twig->addFilter(new \Twig\TwigFilter('cs_type',
function($type, $fld = null)
{
return cs_type($type, $fld);
}
));
$twig->addFilter(new \Twig\TwigFilter('cs_simple_type',
function($type)
{
if($type instanceof \mtgArrType)
return cs_simple_type($type->getValue());
else
return cs_simple_type($type);
return cs_type($type);
}
));
$twig->addFilter(new \Twig\TwigFilter('cs_type_prefix',
@ -201,13 +136,13 @@ function _add_twig_support(\Twig\Environment $twig)
$twig->addFunction(new \Twig\TwigFunction('get_diff_related_units',
function($o)
{
return get_diff_all_related_structs($o);
return get_diff_related_units($o);
}
));
$twig->addFunction(new \Twig\TwigFunction('get_all_declarable_accessor_interfaces',
$twig->addFunction(new \Twig\TwigFunction('get_all_accessor_interfaces',
function($o)
{
return get_all_declarable_accessor_interfaces($o);
return get_all_accessor_interfaces($o);
}
));
$twig->addFunction(new \Twig\TwigFunction('get_accessor_interfaces',
@ -218,18 +153,14 @@ function _add_twig_support(\Twig\Environment $twig)
));
}
function cs_type(\mtgType $type, $fld = null)
function cs_type(\mtgType $type)
{
if($type instanceof \mtgBuiltinType || $type instanceof \mtgUserType)
{
if($fld != null && $fld->hasToken("flt_i18n"))
return "MetaI18NString";
return cs_simple_type($type);
}
else if($type instanceof \mtgArrType)
return "List<" . cs_simple_type($type->getValue()) . ">";
else
throw new Exception("Unknown type '{$type->getName()}'");
throw new Exception("Unknown type '{$type}'");
}
function cs_simple_type(\mtgType $type)
@ -265,11 +196,11 @@ function cs_simple_type(\mtgType $type)
case "bool":
return "bool";
case "blob":
return "ArraySegment<byte>";
return "byte[]";
}
throw new Exception("Unknown type '{$type}'");
}
if($type instanceof \mtgUserType && $type->hasToken("bhl_native_class"))
if($type->hasToken("bhl_native_class"))
return $type->getToken("bhl_native_class");
return $type->getName();
}
@ -307,7 +238,7 @@ function cs_type_prefix(\mtgType $type)
case "blob":
return "Blob";
default:
throw new Exception("Unknown type '{$type->getName()}'");
throw new Exception("Unknown type '{$type}'");
}
}
@ -376,14 +307,14 @@ function var_reset($name, \mtgType $type, $default = null)
$str .= " = ".trim($default, '"').";";
}
else
$str .= ' = default;';
$str .= ' = null;';
}
else
throw new Exception("Unknown type '$type'");
}
else if($type instanceof \mtgArrType)
{
$str = "if($name == null) $name = new(); else $name.Clear();";
$str = "Meta.ClearList(ref $name);";
if($default)
{
@ -421,83 +352,80 @@ function var_reset($name, \mtgType $type, $default = null)
{
$is_pod = $type->hasToken('POD');
if($is_pod)
$str .= "$name.Reset(); ";
$str .= "$name.reset(); ";
else
$str .= "if($name == null) $name = new(); else $name.Reset(); ";
$str .= "Meta.Reset(ref $name); ";
}
if($default)
{
$default_val = is_array($default) ? $default : json_decode($default, true);
if(is_array($default_val))
$default = is_array($default) ? $default : json_decode($default, true);
if(is_array($default))
{
foreach($default_val as $k => $v)
foreach($default as $k => $v)
{
$kf = $type->getField($k);
$str .= var_reset("$name." . $kf->getName(), $kf->getType(), $v);
}
}
else if(is_null_str($default))
else if($default === null)
$str .= "$name = null; ";
else
throw new Exception("Bad default value for struct: " . var_export($default, true));
}
}
else
throw new Exception("Bad type '{$type->getName()}'");
throw new Exception("Bad type '$type'");
return $str;
}
function is_null_str($default)
{
return is_string($default) && strtolower($default) === 'null';
return is_string($default) && json_decode($default, true) === null;
}
function var_sync($fname, \mtgType $type, $buf, array $tokens, $opts)
{
$key_name = array_key_exists('alias', $tokens) ? $tokens['alias'] : $fname;
$optional = array_key_exists('optional', $tokens);
if($optional)
$opts .= " | MetaSyncFieldOpts.SKIP_OPTIONAL";
$str = '';
if($type instanceof \mtgBuiltinType)
{
if($type->isString() && array_key_exists('flt_i18n', $tokens))
$str .= "$fname.SyncSingular({$buf}, \"{$key_name}\", {$opts});\n"; //TODO: plurals support
else
$str .= "MetaIO.Sync({$buf}, ref {$fname}, \"{$key_name}\", {$opts});\n";
$str .= "Meta.Sync({$buf}, ref {$fname}, {$opts});\n";
}
else if($type instanceof \mtgMetaStruct)
{
if(array_key_exists('virtual', $tokens))
$str .= "{$fname} = MetaIO.SyncGeneric({$buf}, {$fname}, \"{$key_name}\", {$opts});\n";
$str .= "{$fname} = ({$type->getName()})Meta.SyncGeneric({$buf}, {$fname}, {$opts});\n";
else
$str .= "MetaIO.Sync({$buf}, ref {$fname}, \"{$key_name}\", {$opts});\n";
$str .= "Meta.Sync({$buf}, ref {$fname}, {$opts});\n";
}
else if($type instanceof \mtgMetaEnum)
{
$str .= "int __tmp_{$fname} = (int)$fname;\n";
$str .= "MetaIO.Sync({$buf}, ref __tmp_{$fname}, \"{$key_name}\", {$opts});\n";
$str .= "if($buf.is_read) {$fname} = (".cs_type($type).")__tmp_{$fname};\n";
$str .= "Meta.Sync({$buf}, ref __tmp_{$fname}, {$opts});\n";
$str .= "if($buf.is_read) {$fname} = ({$type->getName()})__tmp_{$fname};\n";
}
else if($type instanceof \mtgArrType)
{
if(array_key_exists('virtual', $tokens))
$str .= "MetaIO.SyncGeneric({$buf}, {$fname}, \"{$key_name}\", {$opts});\n";
$str .= "Meta.SyncGeneric({$buf}, {$fname}, {$opts});\n";
else
{
if($type->getValue() instanceof \mtgMetaEnum)
{
$str .= "{\n";
$str .= "var tmp_enums_list = new List<int>(); if(!{$buf}.is_read) { foreach(var _enum_tmp in {$fname}) tmp_enums_list.Add((int)_enum_tmp); }\n";
$str .= "MetaIO.Sync({$buf}, tmp_enums_list, \"{$key_name}\", {$opts});\n";
$str .= "if({$buf}.is_read) { {$fname}.Clear(); foreach(var _int_tmp in tmp_enums_list) {$fname}.Add(({$type->getValue()->getName()}) _int_tmp); }\n";
$str .= "}\n";
$str .= "Meta.tmp_enums_list.Clear(); if(!{$buf}.is_read) { foreach(var _enum_tmp in {$fname}) Meta.tmp_enums_list.Add((int)_enum_tmp); }\n";
$str .= "Meta.Sync({$buf}, Meta.tmp_enums_list, {$opts});\n";
$str .= "if({$buf}.is_read) foreach(var _int_tmp in Meta.tmp_enums_list) { {$fname}.Add(({$type->getValue()->getName()}) _int_tmp); }\n";
}
else
$str .= "MetaIO.Sync({$buf}, {$fname}, \"{$key_name}\", {$opts});\n";
$str .= "Meta.Sync({$buf}, {$fname}, {$opts});\n";
}
}
else
throw new Exception("Unknown type '{$type->getName()}'");
throw new Exception("Unknown type '$type'");
return $str;
}
@ -547,8 +475,17 @@ function fields_count_self(\mtgMetaStruct $struct)
function is_primary_field(\mtgMetaStruct $struct, $fld)
{
$primary_fields = array();
if($struct->hasToken("table_pkey"))
$primary_fields = explode(",", $struct->getToken("table_pkey"));
if($struct->hasToken("id"))
$primary_fields[] = $struct->getToken("id");
if($struct->hasToken("owner"))
$primary_fields[] = $struct->getToken("owner");
if($struct->hasToken("pkey"))
{
foreach(explode(",", $struct->getToken("pkey")) as $name)
$primary_fields[] = $name;
}
return in_array($fld->getName(), $primary_fields);
}
@ -575,36 +512,6 @@ function get_diff_related_units(\mtgMetaStruct $struct)
return $result;
}
function get_diff_all_related_structs(\mtgMetaStruct $struct): array
{
$result = array_reduce($struct->getFields(), function(array $structs, \mtgMetaField $field) {
if($field->hasToken('nodiff'))
{
return $structs;
}
$type = $field->getType();
if($type instanceof \mtgArrType)
{
$type = $type->getValue();
}
if($type instanceof \mtgMetaStruct)
{
$structs[] = $type;
}
return $structs;
}, []);
foreach($result as $s)
{
$result = array_merge(get_diff_all_related_structs($s), $result);
}
return $result;
}
class AccessorInterfaceField
{
public $field;
@ -617,11 +524,6 @@ class AccessorInterfaceField
return $this->field->getName();
}
function hasToken($token)
{
return $this->field->hasToken($token);
}
function getCamelFieldName()
{
$name_parts = explode("_", $this->field->getName());
@ -648,26 +550,9 @@ class AccessorInterface
}
}
//NOTE: by default, accessor interface declarations will be generated in the autogen bundle.
//If you want your metagen struct to implement some existing C# inteface
//use quotes and prefix the fully-qualified interface name with "!", e.g. like this:
//
// ticket : string @cs_accessor_interface:"!BitGames.ServerIntegration.IRpcWithAuth"
//
function get_accessor_interfaces(\mtgMetaStruct $struct)
{
return get_accessor_interfaces_ex($struct, /* declarable_only = */ false);
}
function get_accessor_interfaces_declarable(\mtgMetaStruct $struct)
{
return get_accessor_interfaces_ex($struct, /* declarable_only = */ true);
}
function get_accessor_interfaces_ex(\mtgMetaStruct $struct, $declarable_only)
{
$ifs = array();
$external_mark = '"!';
foreach($struct->getFields() as $field)
{
$is_accessor = $field->hasToken('cs_accessor_interface');
@ -687,14 +572,6 @@ function get_accessor_interfaces_ex(\mtgMetaStruct $struct, $declarable_only)
elseif($is_propgetset)
$ai->cs_interface_name = $field->getToken('cs_propgetset_interface');
$is_external = strpos($ai->cs_interface_name, $external_mark) === 0;
if($is_external)
{
$ai->cs_interface_name = trim($ai->cs_interface_name, $external_mark);
if($declarable_only)
continue;
}
if(array_key_exists($ai->getUID(), $ifs))
$ai = $ifs[$ai->getUID()];
@ -715,70 +592,19 @@ function get_accessor_interfaces_ex(\mtgMetaStruct $struct, $declarable_only)
return $ifs;
}
function get_all_declarable_accessor_interfaces(\mtgMetaInfo $info)
function get_all_accessor_interfaces(\mtgMetaInfo $info)
{
$all = array();
foreach($info->getUnits() as $unit)
{
if($unit->object instanceof \mtgMetaStruct)
$all = array_merge($all, get_accessor_interfaces_declarable($unit->object));
$all = array_merge($all, get_accessor_interfaces($unit->object));
else if($unit->object instanceof \mtgMetaRPC)
{
$all = array_merge($all, get_accessor_interfaces_declarable($unit->object->getReq()));
$all = array_merge($all, get_accessor_interfaces_declarable($unit->object->getRsp()));
$all = array_merge($all, get_accessor_interfaces($unit->object->getReq()));
$all = array_merge($all, get_accessor_interfaces($unit->object->getRsp()));
}
}
return $all;
}
function get_field_index(\mtgMetaStruct $struct, string $field_name): int
{
$idx = -1;
foreach($struct->getFields() as $field)
{
$idx++;
if($field->getName() == $field_name)
{
return $idx;
}
}
return -1;
}
function paginate($total, $step)
{
//pages are returned as an array where each element is in interval [N, Y)
$pages = array();
$steps = (int)($total/$step);
$rest = $total % $step;
for($i=1;$i<=$steps;++$i)
$pages[] = array(($i-1)*$step, $i*$step);
if($rest != 0)
$pages[] = array(($i-1)*$step, ($i-1)*$step + $rest);
return $pages;
}
//slices array like this: [[idx0,[..]], [idx1,[..]], ...]
function slice_units(array $units, $max)
{
$pages = paginate(sizeof($units), $max);
$sliced = array();
$units_keys = array_keys($units);
foreach($pages as $idx => $page)
{
$slice = array();
for($i = $page[0];$i<$page[1];++$i)
{
$slice[] = $units[$units_keys[$i]];
}
$sliced[$idx] = $slice;
}
return $sliced;
}

View File

@ -1,7 +1,6 @@
//THIS FILE IS GENERATED AUTOMATICALLY, DO NOT TOUCH IT!
using System.Collections.Generic;
using metagen;
using System;
{%- import "macro.twig" as macro -%}
@ -9,18 +8,34 @@ using System;
namespace {{namespace}} {
{% endif %}
{%- for ai in get_all_declarable_accessor_interfaces(meta) ~%}
{{ macro.decl_accessor_interface(ai) }}
{%- endfor ~%}
{{ macro.decl_units(meta) }}
static public class AutogenBundle
{
static public IRpc createRpc(int code)
{
switch(code)
{
{%- for u in meta.getunits ~%}
{%- if u.object is instanceof('\\mtgMetaRPC') ~%}
case {{u.object.code}}: { return new {{u.object.name}}(); }
{%- endif ~%}
{%- endfor ~%}
default:
{
return null;
}
}
}
static public IMetaStruct createById(uint class_id)
{
switch(class_id)
{
{%- for t in meta.factorytypes ~%}
case {{t.classid}}: { return new {{t.fullname}}(); }
{%- for u in meta.getunits ~%}
{%- if u.object is instanceof('\\mtgMetaStruct')~%}
case {{u.object.classid}}: { return new {{u.object.name}}(); }
{%- endif ~%}
{%- endfor ~%}
default:
{
@ -33,8 +48,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

@ -1,146 +0,0 @@
//THIS FILE IS GENERATED AUTOMATICALLY, DO NOT TOUCH IT!
using System;
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 %}
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') %}
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
{}
{%- 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)}}
}
{%- endif ~%}
{%- endfor ~%}
}

View File

@ -1,16 +0,0 @@
//THIS FILE IS GENERATED AUTOMATICALLY, DO NOT TOUCH IT!
using System.Collections.Generic;
using metagen;
using System;
{%- import "macro.twig" as macro -%}
{% if namespace is defined ~%}
namespace {{namespace}} {
{% endif %}
{{ macro.decl_units(slice_units) }}
{% if namespace is defined ~%}
} //namespace {{namespace}}
{% endif %}

View File

@ -1,19 +1,28 @@
{% macro decl_units(units) %}
{%- for u in units ~%}
{% macro decl_units(meta) %}
{%- for u in meta.getunits ~%}
{%- if u.object is instanceof('\\mtgMetaStruct') -%}
{{ _self.decl_struct(u.object) }}
{%- elseif u.object is instanceof('\\mtgMetaEnum') -%}
{{ _self.decl_enum(u.object) }}
{%- elseif u.object is instanceof('\\mtgMetaRPC') -%}
{{ _self.decl_rpc(u.object) }}
{%- endif ~%}
{%- endfor ~%}
{%- for ai in get_all_accessor_interfaces(meta) ~%}
{{ _self.decl_accessor_interface(ai) }}
{%- endfor ~%}
{% endmacro %}
{% macro decl_struct(o, extra = '') %}
{% set pkey_fields = token(o, 'table_pkey')|split(',') %}
{% set pkey = pkey_fields|filter(f => f|length > 0)|map(f => o.getField(f)) %}
{% if has_token(o, 'table') %}
{% set pkey_fields = token(o, 'table_pkey')|split(',') %}
{% set pkey = pkey_fields|map(f => o.fields[f]) %}
{% endif %}
{%- if o.parent and has_token(o, 'POD') -%}
{{Error("@POD structs can't have a parent: " ~ o.name)}}
@ -25,8 +34,6 @@
{{_self.attributes(o)}}
public {{_self.struct_type(o)}} {{o.name}} {{_self.base_struct_class(o)}}
{
{{token(o, 'cs_embed', '')}}
{{_self.decl_struct_fields(o)}}
public {{o.parent ? 'new'}} const uint STATIC_CLASS_ID = {{o.classid}};
@ -39,45 +46,58 @@ public {{_self.struct_type(o)}} {{o.name}} {{_self.base_struct_class(o)}}
{{_self.comment_POD_begin(o)}}
public {{o.name}}()
{
Reset();
reset();
}
{{_self.comment_POD_end(o)}}
public {{_self.override(o)}} void Reset()
public {{_self.override(o)}} void reset()
{
{{_self.struct_fields_reset(o)}}
}
{{_self.comment_non_cloneable_begin(o)}}
public void CloneTo(ref {{o.name}} dst)
public {{_self.virtual_clone(o)}} void copy(IMetaStruct other)
{
MetaIO.Clone(this, ref dst, AutogenBundle.createById);
copyFrom(({{o.name}})other);
}
public {{_self.virtual_clone(o)}} IMetaStruct Clone()
public void copyFrom({{o.name}} other)
{
var dst = new {{o.name}}();
CloneTo(ref dst);
return dst;
var ctx = Meta.PrepareForClone(ref other);
ctx.factory = AutogenBundle.createById;
ctx.reader.BeginContainer();
reset();
syncFields(ctx);
ctx.reader.EndContainer();
{%- if has_token(o, 'bitfields') ~%}
fields_mask = other.fields_mask;
{%- endif ~%}
}
public {{_self.virtual_clone(o)}} IMetaStruct clone()
{
var copy = new {{o.name}}();
copy.copy(this);
return copy;
}
{{_self.comment_non_cloneable_end(o)}}
public {{_self.override(o)}} void SyncFields(MetaSyncContext ctx)
public {{_self.override(o)}} void syncFields(MetaSyncContext ctx)
{
{{_self.sync_fields(o)}}
}
public {{_self.override(o)}} int GetFieldsCount()
public {{_self.override(o)}} int getFieldsCount()
{
return {{fields_count(o)}};
}
public {{_self.override(o)}} int GetDirtyFieldsCount()
public {{_self.override(o)}} int getWritableFieldsCount()
{
{%~ if has_token(o, 'bitfields') ~%}
return fields_mask.GetDirtyFieldsCount();
return fields_mask.GetDirtyFieldsCount() + Meta.MASK_HEADER_FIELDS_COUNT;
{% else %}
return GetFieldsCount();
return {{fields_count(o)}};
{%- endif ~%}
}
@ -124,12 +144,7 @@ public class {{o.name}}Comparer : IComparer<{{o.name}}>
{
int result;
{% set comparable = pkey %}
{% if comparable|length > 1 %}
{% set comparable = comparable [1:] %}
{% endif %}
{% for f in comparable %}
{% for f in pkey %}
{% if not loop.first %}
if(result == 0)
{% endif %}
@ -154,41 +169,27 @@ public FieldsMask fields_mask;
{%- macro decl_struct_field(o, f) -%}
{{_self.attributes(f)}}
public {{f.type|cs_type(f)|obscure_type(f)}} {{f.name}} {% if not has_token(o, 'POD') -%} {{_self.decl_init_value(f)}} {%- endif -%};
public {{f.type|cs_type|obscure_type(f)}} {{f.name}} {% if not has_token(o, 'POD') -%} {{_self.decl_init_value(f)}} {%- endif -%};
{%- endmacro -%}
{% macro decl_init_value(f) %}
{%- if f.type is instanceof('\\mtgBuiltinType') -%}
{%- if f.type.isstring -%}
{% if has_token(f, 'flt_i18n') -%}
{%- if has_token(f, 'default') -%}
= new({{token(f, 'default')}})
{%- else -%}
= new()
{%- endif -%}
{%- else -%}
= ""
{%- endif ~%}
{%- endif -%}
{%- if f.type.isstring -%} = ""{%- endif -%}
{%- else -%}
{%- if has_token(f, 'default') and token(f, 'default') == 'null' -%}
/*null*/
{%- else -%}
= new {{f.type|cs_type(f)}}()
= new {{f.type|cs_type}}()
{%- endif -%}
{%- endif -%}
{% endmacro %}
{% macro struct_fields_reset(o) %}
{%- if o.parent ~%}
base.Reset();
base.reset();
{%- endif -%}
{%- for f in o.fields ~%}
{% if has_token(f, 'flt_i18n') -%}
{{f.name}}?.Reset();
{%- else ~%}
{{var_reset(f.name, f.type, token_or(f, 'default', null))}}
{%- endif ~%}
{%- endfor -%}
{%- if has_token(o, 'bitfields') ~%}
ResetFieldMask();
@ -196,12 +197,6 @@ ResetFieldMask();
{% endmacro %}
{%- macro attributes(o) %}
{% if has_token(o, "cs_obsolete") %}
/// <summary>
/// {{token(o, "cs_obsolete")}}
/// </summary>
[Obsolete]
{% endif %}
{%- if has_token(o, 'cs_attributes') -%}
{%- for attr in token(o, 'cs_attributes')|split(',') -%}
[{{attr}}]
@ -220,12 +215,6 @@ ResetFieldMask();
: {{o.parent ? o.parent.name : 'BaseMetaStruct'}}
{{- has_token(o, 'cloneable') and not o.parent ? ', IMetaCloneable' -}}
{%- endif -%}
{%- if has_token(o, "cs_implements") -%}
{%- for ifs in token(o, "cs_implements")|split(',') -%}
, {{ifs}}
{%- endfor -%}
{%- endif -%}
{%- for ai in get_accessor_interfaces(o) -%}
, {{ai.interfacename}}
{%- endfor -%}
@ -280,11 +269,11 @@ override
{%- macro sync_fields(o) -%}
{%- if has_token(o, 'bitfields') ~%}
var bitctx = new MetaIO.BitfieldsContext(ctx, fields_mask);
var bitctx = new Meta.BitfieldsContext(ctx, fields_mask);
bitctx.SyncMaskHeader();
{%- endif -%}
{%- if o.parent ~%}
base.SyncFields(ctx);
base.syncFields(ctx);
{%- endif -%}
{%- for f in o.fields ~%}
{{var_sync(f.name, f.type, 'ctx', f.tokens, get_sync_opts(o, 'bitctx'))}}
@ -299,27 +288,14 @@ base.SyncFields(ctx);
}
public void SetPrimaryFieldsChanged()
{
SetPrimaryFieldsChanged(ref this.fields_mask);
}
static void SetPrimaryFieldsChanged(ref FieldsMask mask)
{
{%- for f in o.fields ~%}
{%- if is_primary_field(o, f) ~%}
MetaIO.SetFieldDirty(ref mask, {{loop.index0}});
Meta.SetFieldDirty(ref fields_mask, {{loop.index0}});
{%- endif -%}
{%- endfor ~%}
}
public bool IsContentDirty()
{
int fields_amount = {{fields_count(o)}};
var primary_id_mask = FieldsMask.MakeClean(fields_amount);
SetPrimaryFieldsChanged(ref primary_id_mask);
return FieldsMask.CheckContentsChanged(fields_amount, ref primary_id_mask, ref fields_mask);
}
public void SetDirtyMask()
{
fields_mask = FieldsMask.MakeDirty(fields_amount: {{fields_count(o)}});
@ -356,45 +332,38 @@ public enum {{o.name}}
}
{% endmacro %}
{% macro decl_rpc(o, is_global = true) %}
{% macro decl_rpc(o) %}
{% if is_global %}
{{_self.decl_struct(o.req)}}
{{_self.decl_struct(o.rsp)}}
{% endif %}
public class {{o.name}} : IRpc
{
{% if not is_global %}
{{_self.decl_struct(o.req)}}
{{_self.decl_struct(o.rsp)}}
{% endif %}
public IRpcError error = null;
public {{o.req.name}} req = new {{o.req.name}}();
public {{o.rsp.name}} rsp = new {{o.rsp.name}}();
public int GetCode()
public int getCode()
{
return {{o.code}};
}
public void SetError(IRpcError error)
public void setError(IRpcError error)
{
this.error = error;
}
public IRpcError GetError()
public IRpcError getError()
{
return error;
}
public IMetaStruct GetRequest()
public IMetaStruct getRequest()
{
return req as IMetaStruct;
}
public IMetaStruct GetResponse()
public IMetaStruct getResponse()
{
return rsp as IMetaStruct;
}
@ -407,9 +376,9 @@ public class {{o.name}} : IRpc
public bool GetDiff({{o.name}} old, {{o.name}} diff = null, {{o.name}}RemovedIds removed = null)
{
if(diff != null)
diff.Reset();
diff.reset();
if(removed != null)
removed.Reset();
removed.reset();
bool no_changes = true;
{% set field_idx = -1 %}
@ -417,25 +386,20 @@ public class {{o.name}} : IRpc
{% set field_idx = field_idx + 1 %}
{%- if not has_token(f, 'nodiff') -%}
{{_self.field_diff(f, field_idx, o)}}
{{_self.field_diff(f, field_idx)}}
{%- endif -%}
{% endfor %}
return !no_changes;
}
{% set unique_diff_units = [] %}
{% for u in get_diff_related_units(o) %}
{% if u not in unique_diff_units %}
{{_self.diff_methods_for(u)}}
{% set unique_diff_units = unique_diff_units|merge([u]) %}
{% endif %}
{{_self.diff_methods_for(u)}}
{% endfor %}
{% endmacro %}
{%- macro diff_methods_for(u) -%}
{% if has_token(u, 'table') %}
static public bool DiffOne(ref {{u.name}} curr, {{u.name}} old)
{
return !{{_self.compare_func_name(u)}}(ref curr, old);
@ -459,14 +423,13 @@ public class {{o.name}} : IRpc
if(old < item)
{
if(removed != null)
removed.Add(old.MakeId());
removed.Add(old.MakeId());
old_i++;
has_diff = true;
}
else if(old > item)
{
item.SetDirtyMaskDeep();
item.SetDirtyMask();
if(changed != null)
changed.Add(item);
i++;
@ -474,7 +437,7 @@ public class {{o.name}} : IRpc
}
else
{
if(!{{_self.compare_func_name(u)}}(ref item, old) && item.IsContentDirty())
if(!{{_self.compare_func_name(u)}}(ref item, old))
{
if(changed != null)
changed.Add(item);
@ -490,7 +453,7 @@ public class {{o.name}} : IRpc
for(; i < items.Count; i++)
{
var item = items[i];
item.SetDirtyMaskDeep();
item.SetDirtyMask();
if(changed != null)
changed.Add(item);
has_diff = true;
@ -499,19 +462,17 @@ public class {{o.name}} : IRpc
//old items leftovers are considered removed
for(; old_i < old_items.Count; old_i++)
{
if(removed != null)
removed.Add(old_items[old_i].MakeId());
removed.Add(old_items[old_i].MakeId());
has_diff = true;
}
//if(changed.Count > 0)
// MetaIO.LogDebug("Changed in collection {{u.name}}");
// Meta.LogDebug("Changed in collection {{u.name}}");
//if(removed_ids.Count > 0)
// MetaIO.LogDebug("Removed in collection {{u.name}}");
// Meta.LogDebug("Removed in collection {{u.name}}");
return has_diff;
}
{% endif %}
public static bool {{_self.compare_func_name(u)}}(ref {{u.name}} a, {{u.name}} b)
{
@ -539,127 +500,33 @@ public class {{o.name}} : IRpc
{{has_token(o, 'bitfields')?'CompareAndMarkFields':'IsEqual'}}
{%- endmacro %}
{% macro field_compare_array_bitfields_nested_diffable(o, f, field_idx) %}
{
int {{f.name}}_count_a = a.{{f.name}} == null ? 0 : a.{{f.name}}.Count;
int {{f.name}}_count_b = b.{{f.name}} == null ? 0 : b.{{f.name}}.Count;
{%- macro field_compare(o, f, field_idx) -%}
//For NESTED arrays of DIFFABLE structs:
//for DB compatibility we must add the WHOLE ARRAY to the diff if ANY ELEMENT of the array has changed.
//That means marking each element's fields_mask as dirty
//Changed size means the array has changed, no furhter checks required.
bool {{f.name}}_equal = {{f.name}}_count_a == {{f.name}}_count_b;
//For same size arrays - check contents for changes.
if({{f.name}}_equal)
{
for(int i=0;i<{{f.name}}_count_a && i<{{f.name}}_count_b;++i)
{
var tmp_{{f.name}} = a.{{f.name}}[i];
if(!CompareAndMarkFields(ref tmp_{{f.name}}, b.{{f.name}}[i]))
{
{{f.name}}_equal = false;
break;
}
}
}
//If change detected - mark top-level field as changed and add the whole array to the diff
if(!{{f.name}}_equal)
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
for(int i=0; i<{{f.name}}_count_a;++i)
{
var tmp_{{f.name}} = a.{{f.name}}[i];
tmp_{{f.name}}.SetDirtyMaskDeep();
a.{{f.name}}[i] = tmp_{{f.name}};
is_equal = false;
}
}
}
{%- endmacro %}
{% macro field_compare_array_bitfields_nested_plain(o, f, field_idx) %}
if(a.{{f.name}} == null ||
b.{{f.name}} == null ||
a.{{f.name}}.Count != b.{{f.name}}.Count)
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
}
else
{
for(int i=0;i<a.{{f.name}}.Count;++i)
{
{%- if f.type.value is instanceof('\\mtgMetaStruct') ~%}
var tmp_{{f.name}} = a.{{f.name}}[i];
if(!{{_self.compare_func_name(f.type.value)}}(ref tmp_{{f.name}}, b.{{f.name}}[i]))
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
break;
}
{%- else -%}
if(a.{{f.name}}[i] != b.{{f.name}}[i])
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
break;
}
{%- endif -%}
}
}
{%- endmacro %}
{% macro field_compare_array_plain(o, f, field_idx) %}
{% if f.type is instanceof('\\mtgArrType') ~%}
if(a.{{f.name}} == null ||
b.{{f.name}} == null ||
a.{{f.name}}.Count != b.{{f.name}}.Count)
return false;
for(int i=0;i<a.{{f.name}}.Count;++i)
{
{%- if f.type.value is instanceof('\\mtgMetaStruct') ~%}
var tmp_{{f.name}} = a.{{f.name}}[i];
if(!{{_self.compare_func_name(f.type.value)}}(ref tmp_{{f.name}}, b.{{f.name}}[i]))
if(!IsEqual(ref a.{{f.name}}[i], b.{{f.name}}[i]))
return false;
{%- else -%}
if(a.{{f.name}}[i] != b.{{f.name}}[i])
if(a.{{f.name}}[i] != b.{{f.name}}[i])
return false;
{%- endif -%}
}
{%- endmacro %}
{%- macro field_compare(o, f, field_idx) -%}
{% if f.type is instanceof('\\mtgArrType') ~%}
{% if has_token(o, 'bitfields') ~%}
{% if has_token(f.type.value, 'bitfields') ~%}
{{_self.field_compare_array_bitfields_nested_diffable(o, f, field_idx)}}
{% else ~%}
{{_self.field_compare_array_bitfields_nested_plain(o, f, field_idx)}}
{% endif ~%}
{% else ~%}
{{_self.field_compare_array_plain(o, f, field_idx)}}
{% endif ~%}
{% elseif f.type is instanceof('\\mtgMetaStruct') ~%}
var tmp_{{f.name}} = a.{{f.name}}[i];
if(!{{_self.compare_func_name(f.type.value)}}(ref tmp_{{f.name}}, b.{{f.name}}))
{% if has_token(o, 'bitfields') ~%}
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
}
{% else ~%}
if(!IsEqual(ref a.{{f.name}}, b.{{f.name}}))
return false;
{% endif ~%}
{% elseif f.type is instanceof('\\mtgBuiltinType') and f.type.isstring ~%}
if((a.{{f.name}} == null ? "" : a.{{f.name}})
!= (b.{{f.name}} == null ? "" : b.{{f.name}}))
{% if has_token(o, 'bitfields') ~%}
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
Meta.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
}
{% else ~%}
@ -669,7 +536,7 @@ public class {{o.name}} : IRpc
if(a.{{f.name}} != b.{{f.name}})
{% if has_token(o, 'bitfields') ~%}
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
Meta.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
}
{% else ~%}
@ -679,11 +546,11 @@ public class {{o.name}} : IRpc
{% endmacro %}
{%- macro field_diff(f, field_idx, o) -%}
{%- macro field_diff(f, field_idx) -%}
{% if f.type is instanceof('\\mtgMetaStruct') %}
{%- if not has_token(f.type, 'POD') -%}
{{Error("Diffable struct '" ~ f.type ~ "' must be POD")}}
Error("Diffable struct '" ~ f.type ~ "' must be POD");
{%- endif -%}
{
@ -694,13 +561,13 @@ public class {{o.name}} : IRpc
if(diff != null)
{
diff.{{f.name}} = __tmp;
MetaIO.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
Meta.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
}
}
}
{% elseif f.type is instanceof('\\mtgArrType') %}
{%- if not has_token(f.type.value, 'POD') -%}
{{Error("Diffable struct '" ~ f.type.value ~ "' must be POD")}}
Error("Diffable struct '" ~ f.type.value ~ "' must be POD");
{%- endif -%}
if(DiffCollection({{f.name}}, old.{{f.name}},
@ -710,9 +577,9 @@ public class {{o.name}} : IRpc
{
no_changes &= false;
if(diff != null)
MetaIO.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
Meta.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
if(removed != null && removed.{{f.name}}.Count > 0)
MetaIO.SetFieldDirty(ref removed.fields_mask, {{ field_index(find_struct(o.name ~ 'RemovedIds'), f.name) }});
Meta.SetFieldDirty(ref removed.fields_mask, {{field_idx}});
}
{% elseif f.type is instanceof('\\mtgBuiltinType') %}
if(diff != null)
@ -722,10 +589,10 @@ public class {{o.name}} : IRpc
{
no_changes &= false;
if(diff != null)
MetaIO.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
Meta.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
}
{% else %}
{{Error("Diff for field '"~ f.name ~"' is not supported") }}
Error("Diff for field '"~ f.name ~"' is not supported");
{% endif %}
{% endmacro %}
@ -735,12 +602,12 @@ public interface {{ai.interfacename}}
{
{%- for aif in ai.fields ~%}
{% if aif.is_accessor -%}
{{aif.field.type|cs_type(aif)}} Get{{aif.camelfieldname}}();
void Set{{aif.camelfieldname}}({{aif.field.type|cs_type(aif)}} v);
{{aif.field.type|cs_type}} Get{{aif.camelfieldname}}();
void Set{{aif.camelfieldname}}({{aif.field.type|cs_type}} v);
{% endif -%}
{%- if aif.is_propget -%}
{{aif.field.type|cs_type(aif)}} {{aif.camelfieldname}} {
{{aif.field.type|cs_type}} {{aif.camelfieldname}} {
{%- if aif.is_propget -%}
get;
{%- endif %}
@ -757,18 +624,18 @@ public interface {{ai.interfacename}}
{%- for ai in get_accessor_interfaces(o) ~%}
{%- for aif in ai.fields ~%}
{% if aif.is_accessor %}
public {{aif.field.type|cs_type(aif)}} Get{{aif.camelfieldname}}()
public {{aif.field.type|cs_type}} Get{{aif.camelfieldname}}()
{
return {{aif.field.name}};
}
public void Set{{aif.camelfieldname}}({{aif.field.type|cs_type(aif)}} v)
public void Set{{aif.camelfieldname}}({{aif.field.type|cs_type}} v)
{
this.{{aif.field.name}} = v;
}
{% endif %}
{% if aif.is_propget or aif.is_propset %}
public {{aif.field.type|cs_type(aif)}} {{aif.camelfieldname}} {
public {{aif.field.type|cs_type}} {{aif.camelfieldname}} {
{%- if aif.is_propget -%}
get => this.{{aif.field.name}};
{%- endif -%}
@ -780,3 +647,4 @@ public interface {{ai.interfacename}}
{% endfor %}
{%- endfor ~%}
{% endmacro %}

View File

@ -1,161 +0,0 @@
{%- macro gen_client(obj) ~%}
public interface IApi : IApiBase
{
{%- for rpc in obj.rpcs ~%}
public IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc);
{%- endfor ~%}
}
public class Api : IApi
{
public IRpcCaller Caller { get; }
public Api(IRpcCaller caller)
{
this.Caller = caller;
}
public virtual IRpcCallBuilder<T> CreateCallBuilder<T>(metagen.IRpc rpc) where T : IRpc
{
return new RpcCallBuilder<T>(Caller, (T)rpc);
}
{%- for rpc in obj.rpcs ~%}
public IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc)
{
var builder = CreateCallBuilder<{{rpc.name}}>(rpc);
return builder;
}
{%- endfor ~%}
}
public class ApiDecoratorBase : IApi
{
private IApi orig;
public IRpcCaller Caller => orig.Caller;
public ApiDecoratorBase(IApi orig)
{
this.orig = orig;
}
protected virtual void OnBefore(IRpc rpc)
{}
{%- for rpc in obj.rpcs ~%}
public virtual IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc)
{
OnBefore(rpc);
return orig.Begin(rpc);
}
{%- endfor ~%}
}
public class MockApiBase : IApi
{
public IRpcCaller Caller { get; private set; }
static protected IRpcCallBuilder<T> Result<T>(T rpc) where T : metagen.IRpc
{
return new MockRpcCallBuilder<T>(rpc);
}
public MockApiBase(IRpcCaller caller)
{
this.Caller = caller;
}
{%- for rpc in obj.rpcs ~%}
public virtual IRpcCallBuilder<{{rpc.name}}> Begin({{rpc.name}} rpc)
{
throw new System.NotImplementedException();
}
{%- endfor ~%}
}
static public class ApiExtensions
{
{%- for rpc in obj.rpcs ~%}
static public {{rpc.name}} Set(this {{rpc.name}} __rpc{%- 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-%}
{%- endfor ~%}
)
{
{%- for fld in rpc.req.fields ~%}
__rpc.req.{{fld.name}} = {{fld.name}};
{%- endfor ~%}
return __rpc;
}
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-%}
{%- endfor ~%}
)
{
var rpc = new {{rpc.name}}();
rpc.Set(
{%- for fld in rpc.req.fields ~%}
{{fld.name}} {%- if not loop.last -%},{%-endif-%}
{%- endfor ~%}
);
return __api.Begin(rpc);
}
{%- endfor ~%}
}
{%- endmacro ~%}
{%- macro gen_server(obj) ~%}
public interface IServer<TConn, TRet>
{
{%- for rpc in obj.rpcs ~%}
public TRet On{{rpc.name}}(TConn conn, {{rpc.name}} rpc);
{%- endfor ~%}
}
public static class Router<TConn, TRet>
{
static public Dictionary<int, Func<IServer<TConn, TRet>, TConn, IRpc, TRet>> Map = new();
static Router()
{
{%- for rpc in obj.rpcs ~%}
Map.Add({{rpc.code}}, (server, conn, rpc) => server.On{{rpc.name}}(conn, ({{rpc.name}})rpc));
{%- endfor ~%}
}
static public TRet DispatchRequest(IServer<TConn, TRet> server, TConn conn, metagen.IRpc rpc)
{
switch(rpc.GetCode())
{
{%- for rpc in obj.rpcs ~%}
case {{rpc.code}}:
return server.On{{rpc.name}}(conn, ({{rpc.name}})rpc);
{%- endfor ~%}
}
throw new Exception("No such RPC handler for: " + rpc.GetCode());
}
}
{%- endmacro ~%}