1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 /** 21 * Contains <b>experimental</b> functionality for generating Thrift IDL files 22 * (.thrift) from existing D data structures, i.e. the reverse of what the 23 * Thrift compiler does. 24 */ 25 module thrift.codegen.idlgen; 26 27 import std.algorithm : find; 28 import std.array : empty, front; 29 import std.conv : to; 30 import std.traits : EnumMembers, isSomeFunction, OriginalType, 31 ParameterTypeTuple, ReturnType; 32 import std.typetuple : allSatisfy, staticIndexOf, staticMap, NoDuplicates, 33 TypeTuple; 34 import thrift.base; 35 import thrift.codegen.base; 36 import thrift.internal.codegen; 37 import thrift.internal.ctfe; 38 import thrift.util.hashset; 39 40 /** 41 * True if the passed type is a Thrift entity (struct, exception, enum, 42 * service). 43 */ 44 alias Any!(isStruct, isException, isEnum, isService) isThriftEntity; 45 46 /** 47 * Returns an IDL string describing the passed »root« entities and all types 48 * they depend on. 49 */ 50 template idlString(Roots...) if (allSatisfy!(isThriftEntity, Roots)) { 51 enum idlString = idlStringImpl!Roots.result; 52 } 53 54 private { 55 template idlStringImpl(Roots...) if (allSatisfy!(isThriftEntity, Roots)) { 56 alias ForAllWithList!( 57 ConfinedTuple!(StaticFilter!(isService, Roots)), 58 AddBaseServices 59 ) Services; 60 61 alias TypeTuple!( 62 StaticFilter!(isEnum, Roots), 63 ForAllWithList!( 64 ConfinedTuple!( 65 StaticFilter!(Any!(isException, isStruct), Roots), 66 staticMap!(CompositeTypeDeps, staticMap!(ServiceTypeDeps, Services)) 67 ), 68 AddStructWithDeps 69 ) 70 ) Types; 71 72 enum result = ctfeJoin( 73 [ 74 staticMap!( 75 enumIdlString, 76 StaticFilter!(isEnum, Types) 77 ), 78 staticMap!( 79 structIdlString, 80 StaticFilter!(Any!(isStruct, isException), Types) 81 ), 82 staticMap!( 83 serviceIdlString, 84 Services 85 ) 86 ], 87 "\n" 88 ); 89 } 90 91 template ServiceTypeDeps(T) if (isService!T) { 92 alias staticMap!( 93 PApply!(MethodTypeDeps, T), 94 FilterMethodNames!(T, __traits(derivedMembers, T)) 95 ) ServiceTypeDeps; 96 } 97 98 template MethodTypeDeps(T, string name) if ( 99 isService!T && isSomeFunction!(MemberType!(T, name)) 100 ) { 101 alias TypeTuple!( 102 ReturnType!(MemberType!(T, name)), 103 ParameterTypeTuple!(MemberType!(T, name)), 104 ExceptionTypes!(T, name) 105 ) MethodTypeDeps; 106 } 107 108 template ExceptionTypes(T, string name) if ( 109 isService!T && isSomeFunction!(MemberType!(T, name)) 110 ) { 111 mixin({ 112 enum meta = find!`a.name == b`(getMethodMeta!T, name); 113 if (meta.empty) return "alias TypeTuple!() ExceptionTypes;"; 114 115 string result = "alias TypeTuple!("; 116 foreach (i, e; meta.front.exceptions) { 117 if (i > 0) result ~= ", "; 118 result ~= "mixin(`T." ~ e.type ~ "`)"; 119 } 120 result ~= ") ExceptionTypes;"; 121 return result; 122 }()); 123 } 124 125 template AddBaseServices(T, List...) { 126 static if (staticIndexOf!(T, List) == -1) { 127 alias NoDuplicates!(BaseServices!T, List) AddBaseServices; 128 } else { 129 alias List AddStructWithDeps; 130 } 131 } 132 133 unittest { 134 interface A {} 135 interface B : A {} 136 interface C : B {} 137 interface D : A {} 138 139 static assert(is(AddBaseServices!(C) == TypeTuple!(A, B, C))); 140 static assert(is(ForAllWithList!(ConfinedTuple!(C, D), AddBaseServices) == 141 TypeTuple!(A, D, B, C))); 142 } 143 144 template BaseServices(T, Rest...) if (isService!T) { 145 static if (isDerivedService!T) { 146 alias BaseServices!(BaseService!T, T, Rest) BaseServices; 147 } else { 148 alias TypeTuple!(T, Rest) BaseServices; 149 } 150 } 151 152 template AddStructWithDeps(T, List...) { 153 static if (staticIndexOf!(T, List) == -1) { 154 // T is not already in the List, so add T and the types it depends on in 155 // the front. Because with the Thrift compiler types can only depend on 156 // other types that have already been defined, we collect all the 157 // dependencies, prepend them to the list, and then prune the duplicates 158 // (keeping the first occurrences). If this requirement should ever be 159 // dropped from Thrift, this could be easily adapted to handle circular 160 // dependencies by passing TypeTuple!(T, List) to ForAllWithList instead 161 // of appending List afterwards, and removing the now unnecessary 162 // NoDuplicates. 163 alias NoDuplicates!( 164 ForAllWithList!( 165 ConfinedTuple!( 166 staticMap!( 167 CompositeTypeDeps, 168 staticMap!( 169 PApply!(MemberType, T), 170 FieldNames!T 171 ) 172 ) 173 ), 174 .AddStructWithDeps, 175 T 176 ), 177 List 178 ) AddStructWithDeps; 179 } else { 180 alias List AddStructWithDeps; 181 } 182 } 183 184 version (unittest) { 185 struct A {} 186 struct B { 187 A a; 188 int b; 189 A c; 190 string d; 191 } 192 struct C { 193 B b; 194 A a; 195 } 196 197 static assert(is(AddStructWithDeps!C == TypeTuple!(A, B, C))); 198 199 struct D { 200 C c; 201 mixin TStructHelpers!([TFieldMeta("c", 0, TReq.IGNORE)]); 202 } 203 static assert(is(AddStructWithDeps!D == TypeTuple!(D))); 204 } 205 206 version (unittest) { 207 // Circles in the type dependency graph are not allowed in Thrift, but make 208 // sure we fail in a sane way instead of crashing the compiler. 209 210 struct Rec1 { 211 Rec2[] other; 212 } 213 214 struct Rec2 { 215 Rec1[] other; 216 } 217 218 static assert(!__traits(compiles, AddStructWithDeps!Rec1)); 219 } 220 221 /* 222 * Returns the non-primitive types T directly depends on. 223 * 224 * For example, CompositeTypeDeps!int would yield an empty type tuple, 225 * CompositeTypeDeps!SomeStruct would give SomeStruct, and 226 * CompositeTypeDeps!(A[B]) both CompositeTypeDeps!A and CompositeTypeDeps!B. 227 */ 228 template CompositeTypeDeps(T) { 229 static if (is(FullyUnqual!T == bool) || is(FullyUnqual!T == byte) || 230 is(FullyUnqual!T == short) || is(FullyUnqual!T == int) || 231 is(FullyUnqual!T == long) || is(FullyUnqual!T : string) || 232 is(FullyUnqual!T == double) || is(FullyUnqual!T == void) 233 ) { 234 alias TypeTuple!() CompositeTypeDeps; 235 } else static if (is(FullyUnqual!T _ : U[], U)) { 236 alias CompositeTypeDeps!U CompositeTypeDeps; 237 } else static if (is(FullyUnqual!T _ : HashSet!E, E)) { 238 alias CompositeTypeDeps!E CompositeTypeDeps; 239 } else static if (is(FullyUnqual!T _ : V[K], K, V)) { 240 alias TypeTuple!(CompositeTypeDeps!K, CompositeTypeDeps!V) CompositeTypeDeps; 241 } else static if (is(FullyUnqual!T == enum) || is(FullyUnqual!T == struct) || 242 is(FullyUnqual!T : TException) 243 ) { 244 alias TypeTuple!(FullyUnqual!T) CompositeTypeDeps; 245 } else { 246 static assert(false, "Cannot represent type in Thrift: " ~ T.stringof); 247 } 248 } 249 } 250 251 /** 252 * Returns an IDL string describing the passed service. IDL code for any type 253 * dependcies is not included. 254 */ 255 template serviceIdlString(T) if (isService!T) { 256 enum serviceIdlString = { 257 string result = "service " ~ T.stringof; 258 static if (isDerivedService!T) { 259 result ~= " extends " ~ BaseService!T.stringof; 260 } 261 result ~= " {\n"; 262 263 foreach (methodName; FilterMethodNames!(T, __traits(derivedMembers, T))) { 264 result ~= " "; 265 266 enum meta = find!`a.name == b`(T.methodMeta, methodName); 267 268 static if (!meta.empty && meta.front.type == TMethodType.ONEWAY) { 269 result ~= "oneway "; 270 } 271 272 alias ReturnType!(MemberType!(T, methodName)) RT; 273 static if (is(RT == void)) { 274 // We special-case this here instead of adding void to dToIdlType to 275 // avoid accepting things like void[]. 276 result ~= "void "; 277 } else { 278 result ~= dToIdlType!RT ~ " "; 279 } 280 result ~= methodName ~ "("; 281 282 short lastId; 283 foreach (i, ParamType; ParameterTypeTuple!(MemberType!(T, methodName))) { 284 static if (!meta.empty && i < meta.front.params.length) { 285 enum havePM = true; 286 } else { 287 enum havePM = false; 288 } 289 290 short id; 291 static if (havePM) { 292 id = meta.front.params[i].id; 293 } else { 294 id = --lastId; 295 } 296 297 string paramName; 298 static if (havePM) { 299 paramName = meta.front.params[i].name; 300 } else { 301 paramName = "param" ~ to!string(i + 1); 302 } 303 304 result ~= to!string(id) ~ ": " ~ dToIdlType!ParamType ~ " " ~ paramName; 305 306 static if (havePM && !meta.front.params[i].defaultValue.empty) { 307 result ~= " = " ~ dToIdlConst(mixin(meta.front.params[i].defaultValue)); 308 } else { 309 // Unfortunately, getting the default value for parameters from a 310 // function alias isn't possible – we can't transfer the default 311 // value to the IDL e.g. for interface Foo { void foo(int a = 5); } 312 // without the user explicitly declaring it in metadata. 313 } 314 result ~= ", "; 315 } 316 result ~= ")"; 317 318 static if (!meta.empty && !meta.front.exceptions.empty) { 319 result ~= " throws ("; 320 foreach (e; meta.front.exceptions) { 321 result ~= to!string(e.id) ~ ": " ~ e.type ~ " " ~ e.name ~ ", "; 322 } 323 result ~= ")"; 324 } 325 326 result ~= ",\n"; 327 } 328 329 result ~= "}\n"; 330 return result; 331 }(); 332 } 333 334 /** 335 * Returns an IDL string describing the passed enum. IDL code for any type 336 * dependcies is not included. 337 */ 338 template enumIdlString(T) if (isEnum!T) { 339 enum enumIdlString = { 340 static assert(is(OriginalType!T : long), 341 "Can only have integer enums in Thrift (not " ~ OriginalType!T.stringof ~ 342 ", for " ~ T.stringof ~ ")."); 343 344 string result = "enum " ~ T.stringof ~ " {\n"; 345 346 foreach (name; __traits(derivedMembers, T)) { 347 result ~= " " ~ name ~ " = " ~ dToIdlConst(GetMember!(T, name)) ~ ",\n"; 348 } 349 350 result ~= "}\n"; 351 return result; 352 }(); 353 } 354 355 /** 356 * Returns an IDL string describing the passed struct. IDL code for any type 357 * dependcies is not included. 358 */ 359 template structIdlString(T) if (isStruct!T || isException!T) { 360 enum structIdlString = { 361 mixin({ 362 string code = ""; 363 foreach (field; getFieldMeta!T) { 364 code ~= "static assert(is(MemberType!(T, `" ~ field.name ~ "`)));\n"; 365 } 366 return code; 367 }()); 368 369 string result; 370 static if (isException!T) { 371 result = "exception "; 372 } else { 373 result = "struct "; 374 } 375 result ~= T.stringof ~ " {\n"; 376 377 // The last automatically assigned id – fields with no meta information 378 // are assigned (in lexical order) descending negative ids, starting with 379 // -1, just like the Thrift compiler does. 380 short lastId; 381 382 foreach (name; FieldNames!T) { 383 enum meta = find!`a.name == b`(getFieldMeta!T, name); 384 385 static if (meta.empty || meta.front.req != TReq.IGNORE) { 386 short id; 387 static if (meta.empty) { 388 id = --lastId; 389 } else { 390 id = meta.front.id; 391 } 392 393 result ~= " " ~ to!string(id) ~ ":"; 394 static if (!meta.empty) { 395 result ~= dToIdlReq(meta.front.req); 396 } 397 result ~= " " ~ dToIdlType!(MemberType!(T, name)) ~ " " ~ name; 398 399 static if (!meta.empty && !meta.front.defaultValue.empty) { 400 result ~= " = " ~ dToIdlConst(mixin(meta.front.defaultValue)); 401 } else static if (__traits(compiles, fieldInitA!(T, name))) { 402 static if (is(typeof(fieldInitA!(T, name))) && 403 !is(typeof(fieldInitA!(T, name)) == void) 404 ) { 405 result ~= " = " ~ dToIdlConst(fieldInitA!(T, name)); 406 } 407 } else static if (is(typeof(fieldInitB!(T, name))) && 408 !is(typeof(fieldInitB!(T, name)) == void) 409 ) { 410 result ~= " = " ~ dToIdlConst(fieldInitB!(T, name)); 411 } 412 result ~= ",\n"; 413 } 414 } 415 416 result ~= "}\n"; 417 return result; 418 }(); 419 } 420 421 private { 422 // This very convoluted way of doing things was chosen because putting the 423 // static if directly into structIdlString caused »not evaluatable at compile 424 // time« errors to slip through even though typeof() was used, resp. the 425 // condition to be true even though the value couldn't actually be read at 426 // compile time due to a @@BUG@@ in DMD 2.055. 427 // The extra »compiled« field in fieldInitA is needed because we must not try 428 // to use != if !is compiled as well (but was false), e.g. for floating point 429 // types. 430 template fieldInitA(T, string name) { 431 static if (mixin("T.init." ~ name) !is MemberType!(T, name).init) { 432 enum fieldInitA = mixin("T.init." ~ name); 433 } 434 } 435 436 template fieldInitB(T, string name) { 437 static if (mixin("T.init." ~ name) != MemberType!(T, name).init) { 438 enum fieldInitB = mixin("T.init." ~ name); 439 } 440 } 441 442 template dToIdlType(T) { 443 static if (is(FullyUnqual!T == bool)) { 444 enum dToIdlType = "bool"; 445 } else static if (is(FullyUnqual!T == byte)) { 446 enum dToIdlType = "byte"; 447 } else static if (is(FullyUnqual!T == double)) { 448 enum dToIdlType = "double"; 449 } else static if (is(FullyUnqual!T == short)) { 450 enum dToIdlType = "i16"; 451 } else static if (is(FullyUnqual!T == int)) { 452 enum dToIdlType = "i32"; 453 } else static if (is(FullyUnqual!T == long)) { 454 enum dToIdlType = "i64"; 455 } else static if (is(FullyUnqual!T : string)) { 456 enum dToIdlType = "string"; 457 } else static if (is(FullyUnqual!T _ : U[], U)) { 458 enum dToIdlType = "list<" ~ dToIdlType!U ~ ">"; 459 } else static if (is(FullyUnqual!T _ : V[K], K, V)) { 460 enum dToIdlType = "map<" ~ dToIdlType!K ~ ", " ~ dToIdlType!V ~ ">"; 461 } else static if (is(FullyUnqual!T _ : HashSet!E, E)) { 462 enum dToIdlType = "set<" ~ dToIdlType!E ~ ">"; 463 } else static if (is(FullyUnqual!T == struct) || is(FullyUnqual!T == enum) || 464 is(FullyUnqual!T : TException) 465 ) { 466 enum dToIdlType = FullyUnqual!(T).stringof; 467 } else { 468 static assert(false, "Cannot represent type in Thrift: " ~ T.stringof); 469 } 470 } 471 472 string dToIdlReq(TReq req) { 473 switch (req) { 474 case TReq.REQUIRED: return " required"; 475 case TReq.OPTIONAL: return " optional"; 476 default: return ""; 477 } 478 } 479 480 string dToIdlConst(T)(T value) { 481 static if (is(FullyUnqual!T == bool)) { 482 return value ? "1" : "0"; 483 } else static if (is(FullyUnqual!T == byte) || 484 is(FullyUnqual!T == short) || is(FullyUnqual!T == int) || 485 is(FullyUnqual!T == long) 486 ) { 487 return to!string(value); 488 } else static if (is(FullyUnqual!T : string)) { 489 return `"` ~ to!string(value) ~ `"`; 490 } else static if (is(FullyUnqual!T == double)) { 491 return ctfeToString(value); 492 } else static if (is(FullyUnqual!T _ : U[], U) || 493 is(FullyUnqual!T _ : HashSet!E, E) 494 ) { 495 string result = "["; 496 foreach (e; value) { 497 result ~= dToIdlConst(e) ~ ", "; 498 } 499 result ~= "]"; 500 return result; 501 } else static if (is(FullyUnqual!T _ : V[K], K, V)) { 502 string result = "{"; 503 foreach (key, val; value) { 504 result ~= dToIdlConst(key) ~ ": " ~ dToIdlConst(val) ~ ", "; 505 } 506 result ~= "}"; 507 return result; 508 } else static if (is(FullyUnqual!T == enum)) { 509 import std.conv; 510 import std.traits; 511 return to!string(cast(OriginalType!T)value); 512 } else static if (is(FullyUnqual!T == struct) || 513 is(FullyUnqual!T : TException) 514 ) { 515 string result = "{"; 516 foreach (name; __traits(derivedMembers, T)) { 517 static if (memberReq!(T, name) != TReq.IGNORE) { 518 result ~= name ~ ": " ~ dToIdlConst(mixin("value." ~ name)) ~ ", "; 519 } 520 } 521 result ~= "}"; 522 return result; 523 } else { 524 static assert(false, "Cannot represent type in Thrift: " ~ T.stringof); 525 } 526 } 527 } 528 529 version (unittest) { 530 enum Foo { 531 a = 1, 532 b = 10, 533 c = 5 534 } 535 536 static assert(enumIdlString!Foo == 537 `enum Foo { 538 a = 1, 539 b = 10, 540 c = 5, 541 } 542 `); 543 } 544 545 546 version (unittest) { 547 struct WithoutMeta { 548 string a; 549 int b; 550 } 551 552 struct WithDefaults { 553 string a = "asdf"; 554 double b = 3.1415; 555 WithoutMeta c; 556 557 mixin TStructHelpers!([ 558 TFieldMeta("c", 1, TReq.init, `WithoutMeta("foo", 3)`) 559 ]); 560 } 561 562 // These are from DebugProtoTest.thrift. 563 struct OneOfEach { 564 bool im_true; 565 bool im_false; 566 byte a_bite; 567 short integer16; 568 int integer32; 569 long integer64; 570 double double_precision; 571 string some_characters; 572 string zomg_unicode; 573 bool what_who; 574 string base64; 575 byte[] byte_list; 576 short[] i16_list; 577 long[] i64_list; 578 579 mixin TStructHelpers!([ 580 TFieldMeta(`im_true`, 1), 581 TFieldMeta(`im_false`, 2), 582 TFieldMeta(`a_bite`, 3, TReq.OPT_IN_REQ_OUT, q{cast(byte)127}), 583 TFieldMeta(`integer16`, 4, TReq.OPT_IN_REQ_OUT, q{cast(short)32767}), 584 TFieldMeta(`integer32`, 5), 585 TFieldMeta(`integer64`, 6, TReq.OPT_IN_REQ_OUT, q{10000000000L}), 586 TFieldMeta(`double_precision`, 7), 587 TFieldMeta(`some_characters`, 8), 588 TFieldMeta(`zomg_unicode`, 9), 589 TFieldMeta(`what_who`, 10), 590 TFieldMeta(`base64`, 11), 591 TFieldMeta(`byte_list`, 12, TReq.OPT_IN_REQ_OUT, q{{ 592 byte[] v; 593 v ~= cast(byte)1; 594 v ~= cast(byte)2; 595 v ~= cast(byte)3; 596 return v; 597 }()}), 598 TFieldMeta(`i16_list`, 13, TReq.OPT_IN_REQ_OUT, q{{ 599 short[] v; 600 v ~= cast(short)1; 601 v ~= cast(short)2; 602 v ~= cast(short)3; 603 return v; 604 }()}), 605 TFieldMeta(`i64_list`, 14, TReq.OPT_IN_REQ_OUT, q{{ 606 long[] v; 607 v ~= 1L; 608 v ~= 2L; 609 v ~= 3L; 610 return v; 611 }()}) 612 ]); 613 } 614 615 struct Bonk { 616 int type; 617 string message; 618 619 mixin TStructHelpers!([ 620 TFieldMeta(`type`, 1), 621 TFieldMeta(`message`, 2) 622 ]); 623 } 624 625 struct HolyMoley { 626 OneOfEach[] big; 627 HashSet!(string[]) contain; 628 Bonk[][string] bonks; 629 630 mixin TStructHelpers!([ 631 TFieldMeta(`big`, 1), 632 TFieldMeta(`contain`, 2), 633 TFieldMeta(`bonks`, 3) 634 ]); 635 } 636 637 static assert(structIdlString!WithoutMeta == 638 `struct WithoutMeta { 639 -1: string a, 640 -2: i32 b, 641 } 642 `); 643 644 import std.algorithm; 645 static assert(structIdlString!WithDefaults.startsWith( 646 `struct WithDefaults { 647 -1: string a = "asdf", 648 -2: double b = 3.141`)); 649 650 static assert(structIdlString!WithDefaults.endsWith( 651 `1: WithoutMeta c = {a: "foo", b: 3, }, 652 } 653 `)); 654 655 static assert(structIdlString!OneOfEach == 656 `struct OneOfEach { 657 1: bool im_true, 658 2: bool im_false, 659 3: byte a_bite = 127, 660 4: i16 integer16 = 32767, 661 5: i32 integer32, 662 6: i64 integer64 = 10000000000, 663 7: double double_precision, 664 8: string some_characters, 665 9: string zomg_unicode, 666 10: bool what_who, 667 11: string base64, 668 12: list<byte> byte_list = [1, 2, 3, ], 669 13: list<i16> i16_list = [1, 2, 3, ], 670 14: list<i64> i64_list = [1, 2, 3, ], 671 } 672 `); 673 674 static assert(structIdlString!Bonk == 675 `struct Bonk { 676 1: i32 type, 677 2: string message, 678 } 679 `); 680 681 static assert(structIdlString!HolyMoley == 682 `struct HolyMoley { 683 1: list<OneOfEach> big, 684 2: set<list<string>> contain, 685 3: map<string, list<Bonk>> bonks, 686 } 687 `); 688 } 689 690 version (unittest) { 691 class ExceptionWithAMap : TException { 692 string blah; 693 string[string] map_field; 694 695 mixin TStructHelpers!([ 696 TFieldMeta(`blah`, 1), 697 TFieldMeta(`map_field`, 2) 698 ]); 699 } 700 701 interface Srv { 702 void voidMethod(); 703 int primitiveMethod(); 704 OneOfEach structMethod(); 705 void methodWithDefaultArgs(int something); 706 void onewayMethod(); 707 void exceptionMethod(); 708 709 alias .ExceptionWithAMap ExceptionWithAMap; 710 711 enum methodMeta = [ 712 TMethodMeta(`methodWithDefaultArgs`, 713 [TParamMeta(`something`, 1, q{2})] 714 ), 715 TMethodMeta(`onewayMethod`, 716 [], 717 [], 718 TMethodType.ONEWAY 719 ), 720 TMethodMeta(`exceptionMethod`, 721 [], 722 [ 723 TExceptionMeta("a", 1, "ExceptionWithAMap"), 724 TExceptionMeta("b", 2, "ExceptionWithAMap") 725 ] 726 ) 727 ]; 728 } 729 730 interface ChildSrv : Srv { 731 int childMethod(int arg); 732 } 733 734 static assert(idlString!ChildSrv == 735 `exception ExceptionWithAMap { 736 1: string blah, 737 2: map<string, string> map_field, 738 } 739 740 struct OneOfEach { 741 1: bool im_true, 742 2: bool im_false, 743 3: byte a_bite = 127, 744 4: i16 integer16 = 32767, 745 5: i32 integer32, 746 6: i64 integer64 = 10000000000, 747 7: double double_precision, 748 8: string some_characters, 749 9: string zomg_unicode, 750 10: bool what_who, 751 11: string base64, 752 12: list<byte> byte_list = [1, 2, 3, ], 753 13: list<i16> i16_list = [1, 2, 3, ], 754 14: list<i64> i64_list = [1, 2, 3, ], 755 } 756 757 service Srv { 758 void voidMethod(), 759 i32 primitiveMethod(), 760 OneOfEach structMethod(), 761 void methodWithDefaultArgs(1: i32 something = 2, ), 762 oneway void onewayMethod(), 763 void exceptionMethod() throws (1: ExceptionWithAMap a, 2: ExceptionWithAMap b, ), 764 } 765 766 service ChildSrv extends Srv { 767 i32 childMethod(-1: i32 param1, ), 768 } 769 `); 770 }