diff --git a/docs/source/Schemas.md b/docs/source/Schemas.md index 7ac3bc735..4004803de 100755 --- a/docs/source/Schemas.md +++ b/docs/source/Schemas.md @@ -225,6 +225,20 @@ This declaration in the schema will change that to whatever you want: file_extension "ext"; +### RPC interface declarations + +You can declare RPC calls in a schema, that define a set of functions +that take a FlatBuffer as an argument (the request) and return a FlatBuffer +as the response (both of which must be table types): + + rpc_service MonsterStorage { + Store(Monster):StoreResponse; + Retrieve(MonsterId):Monster; + } + +What code this produces and how it is used depends on language and RPC system +used, FlatBuffers itself does not offer this functionality. + ### Comments & documentation May be written as in most C-based languages. Additionally, a triple diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 17d89a42a..342c5cf24 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -306,6 +306,15 @@ struct EnumDef : public Definition { Type underlying_type; }; +struct RPCCall { + std::string name; + StructDef *request, *response; +}; + +struct ServiceDef : public Definition { + SymbolTable calls; +}; + // Container of options that may apply to any of the source/text generators. struct IDLOptions { bool strict_json; @@ -479,6 +488,7 @@ private: FLATBUFFERS_CHECKED_ERROR StartStruct(const std::string &name, StructDef **dest); FLATBUFFERS_CHECKED_ERROR ParseDecl(); + FLATBUFFERS_CHECKED_ERROR ParseService(); FLATBUFFERS_CHECKED_ERROR ParseProtoFields(StructDef *struct_def, bool isextend, bool inside_oneof); FLATBUFFERS_CHECKED_ERROR ParseProtoOption(); @@ -501,6 +511,7 @@ private: public: SymbolTable structs_; SymbolTable enums_; + SymbolTable services_; std::vector namespaces_; std::string error_; // User readable error_ if Parse() == false diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 73dd80e28..ec7e8d3ce 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -152,7 +152,8 @@ std::string Namespace::GetFullyQualifiedName(const std::string &name, TD(FileExtension, 268, "file_extension") \ TD(Include, 269, "include") \ TD(Attribute, 270, "attribute") \ - TD(Null, 271, "null") + TD(Null, 271, "null") \ + TD(Service, 272, "rpc_service") #ifdef __GNUC__ __extension__ // Stop GCC complaining about trailing comma with -Wpendantic. #endif @@ -360,6 +361,10 @@ CheckedError Parser::Next() { token_ = kTokenNull; return NoError(); } + if (attribute_ == "rpc_service") { + token_ = kTokenService; + return NoError(); + } // If not, it is a user-defined identifier: token_ = kTokenIdentifier; return NoError(); @@ -1243,6 +1248,45 @@ CheckedError Parser::ParseDecl() { return NoError(); } +CheckedError Parser::ParseService() { + std::vector service_comment = doc_comment_; + NEXT(); + auto service_name = attribute_; + EXPECT(kTokenIdentifier); + auto &service_def = *new ServiceDef(); + service_def.name = service_name; + service_def.file = file_being_parsed_; + service_def.doc_comment = service_comment; + service_def.defined_namespace = namespaces_.back(); + if (services_.Add(namespaces_.back()->GetFullyQualifiedName(service_name), + &service_def)) + return Error("service already exists: " + service_name); + ECHECK(ParseMetaData(service_def)); + EXPECT('{'); + do { + auto rpc_name = attribute_; + EXPECT(kTokenIdentifier); + EXPECT('('); + Type reqtype, resptype; + ECHECK(ParseTypeIdent(reqtype)); + EXPECT(')'); + EXPECT(':'); + ECHECK(ParseTypeIdent(resptype)); + if (reqtype.base_type != BASE_TYPE_STRUCT || reqtype.struct_def->fixed || + resptype.base_type != BASE_TYPE_STRUCT || resptype.struct_def->fixed) + return Error("rpc request and response types must be tables"); + auto &rpc = *new RPCCall(); + rpc.name = rpc_name; + rpc.request = reqtype.struct_def; + rpc.response = resptype.struct_def; + if (service_def.calls.Add(rpc_name, &rpc)) + return Error("rpc already exists: " + rpc_name); + EXPECT(';'); + } while (token_ != '}'); + NEXT(); + return NoError(); +} + bool Parser::SetRootType(const char *name) { root_struct_def_ = structs_.Lookup(name); if (!root_struct_def_) @@ -1739,6 +1783,8 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, EXPECT(kTokenStringConstant); EXPECT(';'); known_attributes_.insert(name); + } else if (token_ == kTokenService) { + ECHECK(ParseService()); } else { ECHECK(ParseDecl()); } diff --git a/tests/monster_test.fbs b/tests/monster_test.fbs index 27f35f3a1..f30b5d242 100755 --- a/tests/monster_test.fbs +++ b/tests/monster_test.fbs @@ -61,6 +61,11 @@ table Monster { testhashu64_fnv1a:ulong (id:23, hash:"fnv1a_64"); } +rpc_service MonsterStorage { + Store(Monster):Stat; + Retrieve(Stat):Monster; +} + root_type Monster; file_identifier "MONS";