| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /** |
| * Contains <b>experimental</b> functionality for generating Thrift IDL files |
| * (.thrift) from existing D data structures, i.e. the reverse of what the |
| * Thrift compiler does. |
| */ |
| module thrift.codegen.idlgen; |
| |
| import std.algorithm : find; |
| import std.array : empty, front; |
| import std.conv : to; |
| import std.traits : EnumMembers, isSomeFunction, OriginalType, |
| ParameterTypeTuple, ReturnType; |
| import std.typetuple : allSatisfy, staticIndexOf, staticMap, NoDuplicates, |
| TypeTuple; |
| import thrift.base; |
| import thrift.codegen.base; |
| import thrift.internal.codegen; |
| import thrift.internal.ctfe; |
| import thrift.util.hashset; |
| |
| /** |
| * True if the passed type is a Thrift entity (struct, exception, enum, |
| * service). |
| */ |
| alias Any!(isStruct, isException, isEnum, isService) isThriftEntity; |
| |
| /** |
| * Returns an IDL string describing the passed »root« entities and all types |
| * they depend on. |
| */ |
| template idlString(Roots...) if (allSatisfy!(isThriftEntity, Roots)) { |
| enum idlString = idlStringImpl!Roots.result; |
| } |
| |
| private { |
| template idlStringImpl(Roots...) if (allSatisfy!(isThriftEntity, Roots)) { |
| alias ForAllWithList!( |
| ConfinedTuple!(StaticFilter!(isService, Roots)), |
| AddBaseServices |
| ) Services; |
| |
| alias TypeTuple!( |
| StaticFilter!(isEnum, Roots), |
| ForAllWithList!( |
| ConfinedTuple!( |
| StaticFilter!(Any!(isException, isStruct), Roots), |
| staticMap!(CompositeTypeDeps, staticMap!(ServiceTypeDeps, Services)) |
| ), |
| AddStructWithDeps |
| ) |
| ) Types; |
| |
| enum result = ctfeJoin( |
| [ |
| staticMap!( |
| enumIdlString, |
| StaticFilter!(isEnum, Types) |
| ), |
| staticMap!( |
| structIdlString, |
| StaticFilter!(Any!(isStruct, isException), Types) |
| ), |
| staticMap!( |
| serviceIdlString, |
| Services |
| ) |
| ], |
| "\n" |
| ); |
| } |
| |
| template ServiceTypeDeps(T) if (isService!T) { |
| alias staticMap!( |
| PApply!(MethodTypeDeps, T), |
| FilterMethodNames!(T, __traits(derivedMembers, T)) |
| ) ServiceTypeDeps; |
| } |
| |
| template MethodTypeDeps(T, string name) if ( |
| isService!T && isSomeFunction!(MemberType!(T, name)) |
| ) { |
| alias TypeTuple!( |
| ReturnType!(MemberType!(T, name)), |
| ParameterTypeTuple!(MemberType!(T, name)), |
| ExceptionTypes!(T, name) |
| ) MethodTypeDeps; |
| } |
| |
| template ExceptionTypes(T, string name) if ( |
| isService!T && isSomeFunction!(MemberType!(T, name)) |
| ) { |
| mixin({ |
| enum meta = find!`a.name == b`(getMethodMeta!T, name); |
| if (meta.empty) return "alias TypeTuple!() ExceptionTypes;"; |
| |
| string result = "alias TypeTuple!("; |
| foreach (i, e; meta.front.exceptions) { |
| if (i > 0) result ~= ", "; |
| result ~= "mixin(`T." ~ e.type ~ "`)"; |
| } |
| result ~= ") ExceptionTypes;"; |
| return result; |
| }()); |
| } |
| |
| template AddBaseServices(T, List...) { |
| static if (staticIndexOf!(T, List) == -1) { |
| alias NoDuplicates!(BaseServices!T, List) AddBaseServices; |
| } else { |
| alias List AddStructWithDeps; |
| } |
| } |
| |
| unittest { |
| interface A {} |
| interface B : A {} |
| interface C : B {} |
| interface D : A {} |
| |
| static assert(is(AddBaseServices!(C) == TypeTuple!(A, B, C))); |
| static assert(is(ForAllWithList!(ConfinedTuple!(C, D), AddBaseServices) == |
| TypeTuple!(A, D, B, C))); |
| } |
| |
| template BaseServices(T, Rest...) if (isService!T) { |
| static if (isDerivedService!T) { |
| alias BaseServices!(BaseService!T, T, Rest) BaseServices; |
| } else { |
| alias TypeTuple!(T, Rest) BaseServices; |
| } |
| } |
| |
| template AddStructWithDeps(T, List...) { |
| static if (staticIndexOf!(T, List) == -1) { |
| // T is not already in the List, so add T and the types it depends on in |
| // the front. Because with the Thrift compiler types can only depend on |
| // other types that have already been defined, we collect all the |
| // dependencies, prepend them to the list, and then prune the duplicates |
| // (keeping the first occurrences). If this requirement should ever be |
| // dropped from Thrift, this could be easily adapted to handle circular |
| // dependencies by passing TypeTuple!(T, List) to ForAllWithList instead |
| // of appending List afterwards, and removing the now unnecessary |
| // NoDuplicates. |
| alias NoDuplicates!( |
| ForAllWithList!( |
| ConfinedTuple!( |
| staticMap!( |
| CompositeTypeDeps, |
| staticMap!( |
| PApply!(MemberType, T), |
| FieldNames!T |
| ) |
| ) |
| ), |
| .AddStructWithDeps, |
| T |
| ), |
| List |
| ) AddStructWithDeps; |
| } else { |
| alias List AddStructWithDeps; |
| } |
| } |
| |
| version (unittest) { |
| struct A {} |
| struct B { |
| A a; |
| int b; |
| A c; |
| string d; |
| } |
| struct C { |
| B b; |
| A a; |
| } |
| |
| static assert(is(AddStructWithDeps!C == TypeTuple!(A, B, C))); |
| |
| struct D { |
| C c; |
| mixin TStructHelpers!([TFieldMeta("c", 0, TReq.IGNORE)]); |
| } |
| static assert(is(AddStructWithDeps!D == TypeTuple!(D))); |
| } |
| |
| version (unittest) { |
| // Circles in the type dependency graph are not allowed in Thrift, but make |
| // sure we fail in a sane way instead of crashing the compiler. |
| |
| struct Rec1 { |
| Rec2[] other; |
| } |
| |
| struct Rec2 { |
| Rec1[] other; |
| } |
| |
| static assert(!__traits(compiles, AddStructWithDeps!Rec1)); |
| } |
| |
| /* |
| * Returns the non-primitive types T directly depends on. |
| * |
| * For example, CompositeTypeDeps!int would yield an empty type tuple, |
| * CompositeTypeDeps!SomeStruct would give SomeStruct, and |
| * CompositeTypeDeps!(A[B]) both CompositeTypeDeps!A and CompositeTypeDeps!B. |
| */ |
| template CompositeTypeDeps(T) { |
| static if (is(FullyUnqual!T == bool) || is(FullyUnqual!T == byte) || |
| is(FullyUnqual!T == short) || is(FullyUnqual!T == int) || |
| is(FullyUnqual!T == long) || is(FullyUnqual!T : string) || |
| is(FullyUnqual!T == double) || is(FullyUnqual!T == void) |
| ) { |
| alias TypeTuple!() CompositeTypeDeps; |
| } else static if (is(FullyUnqual!T _ : U[], U)) { |
| alias CompositeTypeDeps!U CompositeTypeDeps; |
| } else static if (is(FullyUnqual!T _ : HashSet!E, E)) { |
| alias CompositeTypeDeps!E CompositeTypeDeps; |
| } else static if (is(FullyUnqual!T _ : V[K], K, V)) { |
| alias TypeTuple!(CompositeTypeDeps!K, CompositeTypeDeps!V) CompositeTypeDeps; |
| } else static if (is(FullyUnqual!T == enum) || is(FullyUnqual!T == struct) || |
| is(FullyUnqual!T : TException) |
| ) { |
| alias TypeTuple!(FullyUnqual!T) CompositeTypeDeps; |
| } else { |
| static assert(false, "Cannot represent type in Thrift: " ~ T.stringof); |
| } |
| } |
| } |
| |
| /** |
| * Returns an IDL string describing the passed service. IDL code for any type |
| * dependcies is not included. |
| */ |
| template serviceIdlString(T) if (isService!T) { |
| enum serviceIdlString = { |
| string result = "service " ~ T.stringof; |
| static if (isDerivedService!T) { |
| result ~= " extends " ~ BaseService!T.stringof; |
| } |
| result ~= " {\n"; |
| |
| foreach (methodName; FilterMethodNames!(T, __traits(derivedMembers, T))) { |
| result ~= " "; |
| |
| enum meta = find!`a.name == b`(T.methodMeta, methodName); |
| |
| static if (!meta.empty && meta.front.type == TMethodType.ONEWAY) { |
| result ~= "oneway "; |
| } |
| |
| alias ReturnType!(MemberType!(T, methodName)) RT; |
| static if (is(RT == void)) { |
| // We special-case this here instead of adding void to dToIdlType to |
| // avoid accepting things like void[]. |
| result ~= "void "; |
| } else { |
| result ~= dToIdlType!RT ~ " "; |
| } |
| result ~= methodName ~ "("; |
| |
| short lastId; |
| foreach (i, ParamType; ParameterTypeTuple!(MemberType!(T, methodName))) { |
| static if (!meta.empty && i < meta.front.params.length) { |
| enum havePM = true; |
| } else { |
| enum havePM = false; |
| } |
| |
| short id; |
| static if (havePM) { |
| id = meta.front.params[i].id; |
| } else { |
| id = --lastId; |
| } |
| |
| string paramName; |
| static if (havePM) { |
| paramName = meta.front.params[i].name; |
| } else { |
| paramName = "param" ~ to!string(i + 1); |
| } |
| |
| result ~= to!string(id) ~ ": " ~ dToIdlType!ParamType ~ " " ~ paramName; |
| |
| static if (havePM && !meta.front.params[i].defaultValue.empty) { |
| result ~= " = " ~ dToIdlConst(mixin(meta.front.params[i].defaultValue)); |
| } else { |
| // Unfortunately, getting the default value for parameters from a |
| // function alias isn't possible – we can't transfer the default |
| // value to the IDL e.g. for interface Foo { void foo(int a = 5); } |
| // without the user explicitly declaring it in metadata. |
| } |
| result ~= ", "; |
| } |
| result ~= ")"; |
| |
| static if (!meta.empty && !meta.front.exceptions.empty) { |
| result ~= " throws ("; |
| foreach (e; meta.front.exceptions) { |
| result ~= to!string(e.id) ~ ": " ~ e.type ~ " " ~ e.name ~ ", "; |
| } |
| result ~= ")"; |
| } |
| |
| result ~= ",\n"; |
| } |
| |
| result ~= "}\n"; |
| return result; |
| }(); |
| } |
| |
| /** |
| * Returns an IDL string describing the passed enum. IDL code for any type |
| * dependcies is not included. |
| */ |
| template enumIdlString(T) if (isEnum!T) { |
| enum enumIdlString = { |
| static assert(is(OriginalType!T : long), |
| "Can only have integer enums in Thrift (not " ~ OriginalType!T.stringof ~ |
| ", for " ~ T.stringof ~ ")."); |
| |
| string result = "enum " ~ T.stringof ~ " {\n"; |
| |
| foreach (name; __traits(derivedMembers, T)) { |
| result ~= " " ~ name ~ " = " ~ dToIdlConst(GetMember!(T, name)) ~ ",\n"; |
| } |
| |
| result ~= "}\n"; |
| return result; |
| }(); |
| } |
| |
| /** |
| * Returns an IDL string describing the passed struct. IDL code for any type |
| * dependcies is not included. |
| */ |
| template structIdlString(T) if (isStruct!T || isException!T) { |
| enum structIdlString = { |
| mixin({ |
| string code = ""; |
| foreach (field; getFieldMeta!T) { |
| code ~= "static assert(is(MemberType!(T, `" ~ field.name ~ "`)));\n"; |
| } |
| return code; |
| }()); |
| |
| string result; |
| static if (isException!T) { |
| result = "exception "; |
| } else { |
| result = "struct "; |
| } |
| result ~= T.stringof ~ " {\n"; |
| |
| // The last automatically assigned id – fields with no meta information |
| // are assigned (in lexical order) descending negative ids, starting with |
| // -1, just like the Thrift compiler does. |
| short lastId; |
| |
| foreach (name; FieldNames!T) { |
| enum meta = find!`a.name == b`(getFieldMeta!T, name); |
| |
| static if (meta.empty || meta.front.req != TReq.IGNORE) { |
| short id; |
| static if (meta.empty) { |
| id = --lastId; |
| } else { |
| id = meta.front.id; |
| } |
| |
| result ~= " " ~ to!string(id) ~ ":"; |
| static if (!meta.empty) { |
| result ~= dToIdlReq(meta.front.req); |
| } |
| result ~= " " ~ dToIdlType!(MemberType!(T, name)) ~ " " ~ name; |
| |
| static if (!meta.empty && !meta.front.defaultValue.empty) { |
| result ~= " = " ~ dToIdlConst(mixin(meta.front.defaultValue)); |
| } else static if (__traits(compiles, fieldInitA!(T, name))) { |
| static if (is(typeof(fieldInitA!(T, name))) && |
| !is(typeof(fieldInitA!(T, name)) == void) |
| ) { |
| result ~= " = " ~ dToIdlConst(fieldInitA!(T, name)); |
| } |
| } else static if (is(typeof(fieldInitB!(T, name))) && |
| !is(typeof(fieldInitB!(T, name)) == void) |
| ) { |
| result ~= " = " ~ dToIdlConst(fieldInitB!(T, name)); |
| } |
| result ~= ",\n"; |
| } |
| } |
| |
| result ~= "}\n"; |
| return result; |
| }(); |
| } |
| |
| private { |
| // This very convoluted way of doing things was chosen because putting the |
| // static if directly into structIdlString caused »not evaluatable at compile |
| // time« errors to slip through even though typeof() was used, resp. the |
| // condition to be true even though the value couldn't actually be read at |
| // compile time due to a @@BUG@@ in DMD 2.055. |
| // The extra »compiled« field in fieldInitA is needed because we must not try |
| // to use != if !is compiled as well (but was false), e.g. for floating point |
| // types. |
| template fieldInitA(T, string name) { |
| static if (mixin("T.init." ~ name) !is MemberType!(T, name).init) { |
| enum fieldInitA = mixin("T.init." ~ name); |
| } |
| } |
| |
| template fieldInitB(T, string name) { |
| static if (mixin("T.init." ~ name) != MemberType!(T, name).init) { |
| enum fieldInitB = mixin("T.init." ~ name); |
| } |
| } |
| |
| template dToIdlType(T) { |
| static if (is(FullyUnqual!T == bool)) { |
| enum dToIdlType = "bool"; |
| } else static if (is(FullyUnqual!T == byte)) { |
| enum dToIdlType = "byte"; |
| } else static if (is(FullyUnqual!T == double)) { |
| enum dToIdlType = "double"; |
| } else static if (is(FullyUnqual!T == short)) { |
| enum dToIdlType = "i16"; |
| } else static if (is(FullyUnqual!T == int)) { |
| enum dToIdlType = "i32"; |
| } else static if (is(FullyUnqual!T == long)) { |
| enum dToIdlType = "i64"; |
| } else static if (is(FullyUnqual!T : string)) { |
| enum dToIdlType = "string"; |
| } else static if (is(FullyUnqual!T _ : U[], U)) { |
| enum dToIdlType = "list<" ~ dToIdlType!U ~ ">"; |
| } else static if (is(FullyUnqual!T _ : V[K], K, V)) { |
| enum dToIdlType = "map<" ~ dToIdlType!K ~ ", " ~ dToIdlType!V ~ ">"; |
| } else static if (is(FullyUnqual!T _ : HashSet!E, E)) { |
| enum dToIdlType = "set<" ~ dToIdlType!E ~ ">"; |
| } else static if (is(FullyUnqual!T == struct) || is(FullyUnqual!T == enum) || |
| is(FullyUnqual!T : TException) |
| ) { |
| enum dToIdlType = FullyUnqual!(T).stringof; |
| } else { |
| static assert(false, "Cannot represent type in Thrift: " ~ T.stringof); |
| } |
| } |
| |
| string dToIdlReq(TReq req) { |
| switch (req) { |
| case TReq.REQUIRED: return " required"; |
| case TReq.OPTIONAL: return " optional"; |
| default: return ""; |
| } |
| } |
| |
| string dToIdlConst(T)(T value) { |
| static if (is(FullyUnqual!T == bool)) { |
| return value ? "1" : "0"; |
| } else static if (is(FullyUnqual!T == byte) || |
| is(FullyUnqual!T == short) || is(FullyUnqual!T == int) || |
| is(FullyUnqual!T == long) |
| ) { |
| return to!string(value); |
| } else static if (is(FullyUnqual!T : string)) { |
| return `"` ~ to!string(value) ~ `"`; |
| } else static if (is(FullyUnqual!T == double)) { |
| return ctfeToString(value); |
| } else static if (is(FullyUnqual!T _ : U[], U) || |
| is(FullyUnqual!T _ : HashSet!E, E) |
| ) { |
| string result = "["; |
| foreach (e; value) { |
| result ~= dToIdlConst(e) ~ ", "; |
| } |
| result ~= "]"; |
| return result; |
| } else static if (is(FullyUnqual!T _ : V[K], K, V)) { |
| string result = "{"; |
| foreach (key, val; value) { |
| result ~= dToIdlConst(key) ~ ": " ~ dToIdlConst(val) ~ ", "; |
| } |
| result ~= "}"; |
| return result; |
| } else static if (is(FullyUnqual!T == enum)) { |
| import std.conv; |
| import std.traits; |
| return to!string(cast(OriginalType!T)value); |
| } else static if (is(FullyUnqual!T == struct) || |
| is(FullyUnqual!T : TException) |
| ) { |
| string result = "{"; |
| foreach (name; __traits(derivedMembers, T)) { |
| static if (memberReq!(T, name) != TReq.IGNORE) { |
| result ~= name ~ ": " ~ dToIdlConst(mixin("value." ~ name)) ~ ", "; |
| } |
| } |
| result ~= "}"; |
| return result; |
| } else { |
| static assert(false, "Cannot represent type in Thrift: " ~ T.stringof); |
| } |
| } |
| } |
| |
| version (unittest) { |
| enum Foo { |
| a = 1, |
| b = 10, |
| c = 5 |
| } |
| |
| static assert(enumIdlString!Foo == |
| `enum Foo { |
| a = 1, |
| b = 10, |
| c = 5, |
| } |
| `); |
| } |
| |
| |
| version (unittest) { |
| struct WithoutMeta { |
| string a; |
| int b; |
| } |
| |
| struct WithDefaults { |
| string a = "asdf"; |
| double b = 3.1415; |
| WithoutMeta c; |
| |
| mixin TStructHelpers!([ |
| TFieldMeta("c", 1, TReq.init, `WithoutMeta("foo", 3)`) |
| ]); |
| } |
| |
| // These are from DebugProtoTest.thrift. |
| struct OneOfEach { |
| bool im_true; |
| bool im_false; |
| byte a_bite; |
| short integer16; |
| int integer32; |
| long integer64; |
| double double_precision; |
| string some_characters; |
| string zomg_unicode; |
| bool what_who; |
| string base64; |
| byte[] byte_list; |
| short[] i16_list; |
| long[] i64_list; |
| |
| mixin TStructHelpers!([ |
| TFieldMeta(`im_true`, 1), |
| TFieldMeta(`im_false`, 2), |
| TFieldMeta(`a_bite`, 3, TReq.OPT_IN_REQ_OUT, q{cast(byte)127}), |
| TFieldMeta(`integer16`, 4, TReq.OPT_IN_REQ_OUT, q{cast(short)32767}), |
| TFieldMeta(`integer32`, 5), |
| TFieldMeta(`integer64`, 6, TReq.OPT_IN_REQ_OUT, q{10000000000L}), |
| TFieldMeta(`double_precision`, 7), |
| TFieldMeta(`some_characters`, 8), |
| TFieldMeta(`zomg_unicode`, 9), |
| TFieldMeta(`what_who`, 10), |
| TFieldMeta(`base64`, 11), |
| TFieldMeta(`byte_list`, 12, TReq.OPT_IN_REQ_OUT, q{{ |
| byte[] v; |
| v ~= cast(byte)1; |
| v ~= cast(byte)2; |
| v ~= cast(byte)3; |
| return v; |
| }()}), |
| TFieldMeta(`i16_list`, 13, TReq.OPT_IN_REQ_OUT, q{{ |
| short[] v; |
| v ~= cast(short)1; |
| v ~= cast(short)2; |
| v ~= cast(short)3; |
| return v; |
| }()}), |
| TFieldMeta(`i64_list`, 14, TReq.OPT_IN_REQ_OUT, q{{ |
| long[] v; |
| v ~= 1L; |
| v ~= 2L; |
| v ~= 3L; |
| return v; |
| }()}) |
| ]); |
| } |
| |
| struct Bonk { |
| int type; |
| string message; |
| |
| mixin TStructHelpers!([ |
| TFieldMeta(`type`, 1), |
| TFieldMeta(`message`, 2) |
| ]); |
| } |
| |
| struct HolyMoley { |
| OneOfEach[] big; |
| HashSet!(string[]) contain; |
| Bonk[][string] bonks; |
| |
| mixin TStructHelpers!([ |
| TFieldMeta(`big`, 1), |
| TFieldMeta(`contain`, 2), |
| TFieldMeta(`bonks`, 3) |
| ]); |
| } |
| |
| static assert(structIdlString!WithoutMeta == |
| `struct WithoutMeta { |
| -1: string a, |
| -2: i32 b, |
| } |
| `); |
| |
| import std.algorithm; |
| static assert(structIdlString!WithDefaults.startsWith( |
| `struct WithDefaults { |
| -1: string a = "asdf", |
| -2: double b = 3.141`)); |
| |
| static assert(structIdlString!WithDefaults.endsWith( |
| `1: WithoutMeta c = {a: "foo", b: 3, }, |
| } |
| `)); |
| |
| static assert(structIdlString!OneOfEach == |
| `struct OneOfEach { |
| 1: bool im_true, |
| 2: bool im_false, |
| 3: byte a_bite = 127, |
| 4: i16 integer16 = 32767, |
| 5: i32 integer32, |
| 6: i64 integer64 = 10000000000, |
| 7: double double_precision, |
| 8: string some_characters, |
| 9: string zomg_unicode, |
| 10: bool what_who, |
| 11: string base64, |
| 12: list<byte> byte_list = [1, 2, 3, ], |
| 13: list<i16> i16_list = [1, 2, 3, ], |
| 14: list<i64> i64_list = [1, 2, 3, ], |
| } |
| `); |
| |
| static assert(structIdlString!Bonk == |
| `struct Bonk { |
| 1: i32 type, |
| 2: string message, |
| } |
| `); |
| |
| static assert(structIdlString!HolyMoley == |
| `struct HolyMoley { |
| 1: list<OneOfEach> big, |
| 2: set<list<string>> contain, |
| 3: map<string, list<Bonk>> bonks, |
| } |
| `); |
| } |
| |
| version (unittest) { |
| class ExceptionWithAMap : TException { |
| string blah; |
| string[string] map_field; |
| |
| mixin TStructHelpers!([ |
| TFieldMeta(`blah`, 1), |
| TFieldMeta(`map_field`, 2) |
| ]); |
| } |
| |
| interface Srv { |
| void voidMethod(); |
| int primitiveMethod(); |
| OneOfEach structMethod(); |
| void methodWithDefaultArgs(int something); |
| void onewayMethod(); |
| void exceptionMethod(); |
| |
| alias .ExceptionWithAMap ExceptionWithAMap; |
| |
| enum methodMeta = [ |
| TMethodMeta(`methodWithDefaultArgs`, |
| [TParamMeta(`something`, 1, q{2})] |
| ), |
| TMethodMeta(`onewayMethod`, |
| [], |
| [], |
| TMethodType.ONEWAY |
| ), |
| TMethodMeta(`exceptionMethod`, |
| [], |
| [ |
| TExceptionMeta("a", 1, "ExceptionWithAMap"), |
| TExceptionMeta("b", 2, "ExceptionWithAMap") |
| ] |
| ) |
| ]; |
| } |
| |
| interface ChildSrv : Srv { |
| int childMethod(int arg); |
| } |
| |
| static assert(idlString!ChildSrv == |
| `exception ExceptionWithAMap { |
| 1: string blah, |
| 2: map<string, string> map_field, |
| } |
| |
| struct OneOfEach { |
| 1: bool im_true, |
| 2: bool im_false, |
| 3: byte a_bite = 127, |
| 4: i16 integer16 = 32767, |
| 5: i32 integer32, |
| 6: i64 integer64 = 10000000000, |
| 7: double double_precision, |
| 8: string some_characters, |
| 9: string zomg_unicode, |
| 10: bool what_who, |
| 11: string base64, |
| 12: list<byte> byte_list = [1, 2, 3, ], |
| 13: list<i16> i16_list = [1, 2, 3, ], |
| 14: list<i64> i64_list = [1, 2, 3, ], |
| } |
| |
| service Srv { |
| void voidMethod(), |
| i32 primitiveMethod(), |
| OneOfEach structMethod(), |
| void methodWithDefaultArgs(1: i32 something = 2, ), |
| oneway void onewayMethod(), |
| void exceptionMethod() throws (1: ExceptionWithAMap a, 2: ExceptionWithAMap b, ), |
| } |
| |
| service ChildSrv extends Srv { |
| i32 childMethod(-1: i32 param1, ), |
| } |
| `); |
| } |