| /* |
| * 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. |
| */ |
| |
| #include <string> |
| #include <fstream> |
| #include <iostream> |
| |
| #include "thrift/platform.h" |
| #include "thrift/generate/t_generator.h" |
| |
| using std::map; |
| using std::ofstream; |
| using std::ostringstream; |
| using std::string; |
| using std::vector; |
| using std::set; |
| |
| static const string endl("\n"); // avoid ostream << std::endl flushes |
| static const string SERVICE_RESULT_VARIABLE("result_value"); |
| static const string RESULT_STRUCT_SUFFIX("Result"); |
| static const string RUST_RESERVED_WORDS[] = { |
| "abstract", "alignof", "as", "become", |
| "box", "break", "const", "continue", |
| "crate", "do", "else", "enum", |
| "extern", "false", "final", "fn", |
| "for", "if", "impl", "in", |
| "let", "loop", "macro", "match", |
| "mod", "move", "mut", "offsetof", |
| "override", "priv", "proc", "pub", |
| "pure", "ref", "return", "Self", |
| "self", "sizeof", "static", "struct", |
| "super", "trait", "true", "type", |
| "typeof", "unsafe", "unsized", "use", |
| "virtual", "where", "while", "yield" |
| }; |
| const set<string> RUST_RESERVED_WORDS_SET( |
| RUST_RESERVED_WORDS, |
| RUST_RESERVED_WORDS + sizeof(RUST_RESERVED_WORDS)/sizeof(RUST_RESERVED_WORDS[0]) |
| ); |
| |
| static const string SYNC_CLIENT_GENERIC_BOUND_VARS("<IP, OP>"); |
| static const string SYNC_CLIENT_GENERIC_BOUNDS("where IP: TInputProtocol, OP: TOutputProtocol"); |
| |
| // FIXME: extract common TMessageIdentifier function |
| // FIXME: have to_rust_type deal with Option |
| |
| class t_rs_generator : public t_generator { |
| public: |
| t_rs_generator( |
| t_program* program, |
| const std::map<std::string, std::string>&, |
| const std::string& |
| ) : t_generator(program) { |
| gen_dir_ = get_out_dir(); |
| } |
| |
| /** |
| * Init and close methods |
| */ |
| |
| void init_generator() override; |
| void close_generator() override; |
| |
| /** |
| * Program-level generation functions |
| */ |
| |
| void generate_typedef(t_typedef* ttypedef) override; |
| void generate_enum(t_enum* tenum) override; |
| void generate_const(t_const* tconst) override; |
| void generate_struct(t_struct* tstruct) override; |
| void generate_xception(t_struct* txception) override; |
| void generate_service(t_service* tservice) override; |
| |
| private: |
| // struct type |
| // T_REGULAR: user-defined struct in the IDL |
| // T_ARGS: struct used to hold all service-call parameters |
| // T_RESULT: struct used to hold all service-call returns and exceptions |
| // T_EXCEPTION: user-defined exception in the IDL |
| enum e_struct_type { T_REGULAR, T_ARGS, T_RESULT, T_EXCEPTION }; |
| |
| // Directory to which generated code is written. |
| string gen_dir_; |
| |
| // File to which generated code is written. |
| ofstream_with_content_based_conditional_update f_gen_; |
| |
| // Write the common compiler attributes and module includes to the top of the auto-generated file. |
| void render_attributes_and_includes(); |
| |
| // Create the closure of Rust modules referenced by this service. |
| void compute_service_referenced_modules(t_service *tservice, set<string> &referenced_modules); |
| |
| // Write the rust representation of an enum. |
| void render_enum_definition(t_enum* tenum, const string& enum_name); |
| |
| // Write the impl blocks associated with the traits necessary to convert an enum to/from an i32. |
| void render_enum_conversion(t_enum* tenum, const string& enum_name); |
| |
| // Write the impl block associated with the rust representation of an enum. This includes methods |
| // to write the enum to a protocol, read it from a protocol, etc. |
| void render_enum_impl(const string& enum_name); |
| |
| // Write a simple rust const value (ie. `pub const FOO: foo...`). |
| void render_const_value(const string& name, t_type* ttype, t_const_value* tvalue); |
| |
| // Write a constant list, set, map or struct. These constants require allocation and cannot be defined |
| // using a 'pub const'. As a result, I create a holder struct with a single `const_value` method that |
| // returns the initialized instance. |
| void render_const_value_holder(const string& name, t_type* ttype, t_const_value* tvalue); |
| |
| // Write the actual const value - the right side of a const definition. |
| void render_const_value(t_type* ttype, t_const_value* tvalue, bool is_owned = true); |
| |
| // Write a const struct (returned from `const_value` method). |
| void render_const_struct(t_type* ttype, t_const_value* tvalue); |
| |
| // Write a const list (returned from `const_value` method). |
| void render_const_list(t_type* ttype, t_const_value* tvalue); |
| |
| // Write a const set (returned from `const_value` method). |
| void render_const_set(t_type* ttype, t_const_value* tvalue); |
| |
| // Write a const map (returned from `const_value` method). |
| void render_const_map(t_type* ttype, t_const_value* tvalue); |
| |
| // Write the code to insert constant values into a rust vec or set. The |
| // `insert_function` is the rust function that we'll use to insert the elements. |
| void render_container_const_value( |
| const string& insert_function, |
| t_type* ttype, |
| t_const_value* tvalue |
| ); |
| |
| // Write the rust representation of a thrift struct to the generated file. Set `struct_type` to `T_ARGS` |
| // if rendering the struct used to pack arguments for a service call. When `struct_type` is `T_ARGS` the |
| // struct and its members have module visibility, and all fields are required. When `struct_type` is |
| // anything else the struct and its members have public visibility and fields have the visibility set |
| // in their definition. |
| void render_struct(const string& struct_name, t_struct* tstruct, t_rs_generator::e_struct_type struct_type); |
| |
| // Write the comment block preceding a type definition (and implementation). |
| void render_type_comment(const string& struct_name); |
| |
| // Write the rust representation of a thrift struct. Supports argument structs, result structs, |
| // user-defined structs and exception structs. The exact struct type to be generated is controlled |
| // by the `struct_type` parameter, which (among other things) modifies the visibility of the |
| // written struct and members, controls which trait implementations are generated. |
| void render_struct_definition( |
| const string& struct_name, |
| t_struct* tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ); |
| |
| // Writes the impl block associated with the rust representation of a struct. At minimum this |
| // contains the methods to read from a protocol and write to a protocol. Additional methods may |
| // be generated depending on `struct_type`. |
| void render_struct_impl( |
| const string& struct_name, |
| t_struct* tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ); |
| |
| // Generate a `fn new(...)` for a struct with name `struct_name` and type `t_struct`. The auto-generated |
| // code may include generic type parameters to make the constructor more ergonomic. `struct_type` controls |
| // the visibility of the generated constructor. |
| void render_struct_constructor( |
| const string& struct_name, |
| t_struct* tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ); |
| |
| // Write the `ok_or` method added to all Thrift service call result structs. You can use this method |
| // to convert a struct into a `Result` and use it in a `try!` or combinator chain. |
| void render_result_struct_to_result_method(t_struct* tstruct); |
| |
| // Write the implementations for the `Error` and `Debug` traits. These traits are necessary for a |
| // user-defined exception to be properly handled as Rust errors. |
| void render_exception_struct_error_trait_impls(const string& struct_name, t_struct* tstruct); |
| |
| // Write the implementations for the `Default`. This trait allows you to specify only the fields you want |
| // and use `..Default::default()` to fill in the rest. |
| void render_struct_default_trait_impl(const string& struct_name, t_struct* tstruct); |
| |
| // Write the function that serializes a struct to its wire representation. If `struct_type` is `T_ARGS` |
| // then all fields are considered "required", if not, the default optionality is used. |
| void render_struct_sync_write(t_struct *tstruct, t_rs_generator::e_struct_type struct_type); |
| |
| // Helper function that serializes a single struct field to its wire representation. Unpacks the |
| // variable (since it may be optional) and serializes according to the optionality rules required by `req`. |
| // Variables in auto-generated code are passed by reference. Since this function may be called in |
| // contexts where the variable is *already* a reference you can set `field_var_is_ref` to `true` to avoid |
| // generating an extra, unnecessary `&` that the compiler will have to automatically dereference. |
| void render_struct_field_sync_write( |
| const string &field_var, |
| bool field_var_is_ref, |
| t_field *tfield, |
| t_field::e_req req); |
| |
| // Write the rust function that serializes a single type (i.e. a i32 etc.) to its wire representation. |
| // Variables in auto-generated code are passed by reference. Since this function may be called in |
| // contexts where the variable is *already* a reference you can set `type_var_is_ref` to `true` to avoid |
| // generating an extra, unnecessary `&` that the compiler will have to automatically dereference. |
| void render_type_sync_write(const string &type_var, bool type_var_is_ref, t_type *ttype); |
| |
| // Write a list to the output protocol. `list_variable` is the variable containing the list |
| // that will be written to the output protocol. |
| // Variables in auto-generated code are passed by reference. Since this function may be called in |
| // contexts where the variable is *already* a reference you can set `list_var_is_ref` to `true` to avoid |
| // generating an extra, unnecessary `&` that the compiler will have to automatically dereference. |
| void render_list_sync_write(const string &list_var, bool list_var_is_ref, t_list *tlist); |
| |
| // Write a set to the output protocol. `set_variable` is the variable containing the set that will |
| // be written to the output protocol. |
| // Variables in auto-generated code are passed by reference. Since this function may be called in |
| // contexts where the variable is *already* a reference you can set `set_var_is_ref` to `true` to avoid |
| // generating an extra, unnecessary `&` that the compiler will have to automatically dereference. |
| void render_set_sync_write(const string &set_var, bool set_var_is_ref, t_set *tset); |
| |
| // Write a map to the output protocol. `map_variable` is the variable containing the map that will |
| // be written to the output protocol. |
| // Variables in auto-generated code are passed by reference. Since this function may be called in |
| // contexts where the variable is *already* a reference you can set `map_var_is_ref` to `true` to avoid |
| // generating an extra, unnecessary `&` that the compiler will have to automatically dereference. |
| void render_map_sync_write(const string &map_var, bool map_var_is_ref, t_map *tset); |
| |
| // Return `true` if we need to dereference ths type when writing an element from a container. |
| // Iterations on rust containers are performed as follows: `for v in &values { ... }` |
| // where `v` has type `&RUST_TYPE` All defined functions take primitives by value, so, if the |
| // rendered code is calling such a function it has to dereference `v`. |
| bool needs_deref_on_container_write(t_type* ttype); |
| |
| // Return the variable (including all dereferences) required to write values from a rust container |
| // to the output protocol. For example, if you were iterating through a container and using the temp |
| // variable `v` to represent each element, then `ttype` is the type stored in the container and |
| // `base_var` is "v". The return value is the actual string you will have to use to properly reference |
| // the temp variable for writing to the output protocol. |
| string string_container_write_variable(t_type* ttype, const string& base_var); |
| |
| // Write the code to read bytes from the wire into the given `t_struct`. `struct_name` is the |
| // actual Rust name of the `t_struct`. If `struct_type` is `T_ARGS` then all struct fields are |
| // necessary. Otherwise, the field's default optionality is used. |
| void render_struct_sync_read(const string &struct_name, t_struct *tstruct, t_rs_generator::e_struct_type struct_type); |
| |
| // Write the rust function that deserializes a single type (i.e. i32 etc.) from its wire representation. |
| // Set `is_boxed` to `true` if the resulting value should be wrapped in a `Box::new(...)`. |
| void render_type_sync_read(const string &type_var, t_type *ttype, bool is_boxed = false); |
| |
| // Read the wire representation of a list and convert it to its corresponding rust implementation. |
| // The deserialized list is stored in `list_variable`. |
| void render_list_sync_read(t_list *tlist, const string &list_variable); |
| |
| // Read the wire representation of a set and convert it to its corresponding rust implementation. |
| // The deserialized set is stored in `set_variable`. |
| void render_set_sync_read(t_set *tset, const string &set_variable); |
| |
| // Read the wire representation of a map and convert it to its corresponding rust implementation. |
| // The deserialized map is stored in `map_variable`. |
| void render_map_sync_read(t_map *tmap, const string &map_variable); |
| |
| // Return a temporary variable used to store values when deserializing nested containers. |
| string struct_field_read_temp_variable(t_field* tfield); |
| |
| // Top-level function that calls the various render functions necessary to write the rust representation |
| // of a thrift union (i.e. an enum). |
| void render_union(t_struct* tstruct); |
| |
| // Write the enum corresponding to the Thrift union. |
| void render_union_definition(const string& union_name, t_struct* tstruct); |
| |
| // Write the enum impl (with read/write functions) for the Thrift union. |
| void render_union_impl(const string& union_name, t_struct* tstruct); |
| |
| // Write the `ENUM::write_to_out_protocol` function. |
| void render_union_sync_write(const string &union_name, t_struct *tstruct); |
| |
| // Write the `ENUM::read_from_in_protocol` function. |
| void render_union_sync_read(const string &union_name, t_struct *tstruct); |
| |
| // Top-level function that calls the various render functions necessary to write the rust representation |
| // of a Thrift client. |
| void render_sync_client(t_service* tservice); |
| |
| // Write the trait with the service-call methods for `tservice`. |
| void render_sync_client_trait(t_service *tservice); |
| |
| // Write the trait to be implemented by the client impl if end users can use it to make service calls. |
| void render_sync_client_marker_trait(t_service *tservice); |
| |
| // Write the code to create the Thrift service sync client struct and its matching 'impl' block. |
| void render_sync_client_definition_and_impl(const string& client_impl_name); |
| |
| // Write the code to create the `SyncClient::new` functions as well as any other functions |
| // callers would like to use on the Thrift service sync client. |
| void render_sync_client_lifecycle_functions(const string& client_struct); |
| |
| // Write the code to create the impl block for the `TThriftClient` trait. Since generated |
| // Rust Thrift clients perform all their operations using methods defined in this trait, we |
| // have to implement it for the client structs. |
| void render_sync_client_tthriftclient_impl(const string &client_impl_name); |
| |
| // Write the marker traits for any service(s) being extended, including the one for the current |
| // service itself (i.e. `tservice`) |
| void render_sync_client_marker_trait_impls(t_service *tservice, const string &impl_struct_name); |
| |
| // Generate a list of all the traits this Thrift client struct extends. |
| string sync_client_marker_traits_for_extension(t_service *tservice); |
| |
| // Top-level function that writes the code to make the Thrift service calls. |
| void render_sync_client_process_impl(t_service* tservice); |
| |
| // Write the actual function that calls out to the remote service and processes its response. |
| void render_sync_send_recv_wrapper(t_function* tfunc); |
| |
| // Write the `send` functionality for a Thrift service call represented by a `t_service->t_function`. |
| void render_sync_send(t_function* tfunc); |
| |
| // Write the `recv` functionality for a Thrift service call represented by a `t_service->t_function`. |
| // This method is only rendered if the function is *not* oneway. |
| void render_sync_recv(t_function* tfunc); |
| |
| void render_sync_processor(t_service *tservice); |
| |
| void render_sync_handler_trait(t_service *tservice); |
| void render_sync_processor_definition_and_impl(t_service *tservice); |
| void render_sync_process_delegation_functions(t_service *tservice); |
| void render_sync_process_function(t_function *tfunc, const string &handler_type); |
| void render_process_match_statements(t_service* tservice); |
| void render_sync_handler_succeeded(t_function *tfunc); |
| void render_sync_handler_failed(t_function *tfunc); |
| void render_sync_handler_failed_user_exception_branch(t_function *tfunc); |
| void render_sync_handler_failed_application_exception_branch(t_function *tfunc, const string &app_err_var); |
| void render_sync_handler_failed_default_exception_branch(t_function *tfunc); |
| void render_sync_handler_send_exception_response(t_function *tfunc, const string &err_var); |
| void render_service_call_structs(t_service* tservice); |
| void render_service_call_args_struct(t_function* tfunc); |
| void render_service_call_result_value_struct(t_function* tfunc); |
| |
| string handler_successful_return_struct(t_function* tfunc); |
| |
| // Writes the result of `render_thrift_error_struct` wrapped in an `Err(thrift::Error(...))`. |
| void render_thrift_error( |
| const string& error_kind, |
| const string& error_struct, |
| const string& sub_error_kind, |
| const string& error_message |
| ); |
| |
| // Write a thrift::Error variant struct. Error structs take the form: |
| // ``` |
| // pub struct error_struct { |
| // kind: sub_error_kind, |
| // message: error_message, |
| // } |
| // ``` |
| // A concrete example is: |
| // ``` |
| // pub struct ApplicationError { |
| // kind: ApplicationErrorKind::Unknown, |
| // message: "This is some error message", |
| // } |
| // ``` |
| void render_thrift_error_struct( |
| const string& error_struct, |
| const string& sub_error_kind, |
| const string& error_message |
| ); |
| |
| // Return a string containing all the unpacked service call args given a service call function |
| // `t_function`. Prepends the args with either `&mut self` or `&self` and includes the arg types |
| // in the returned string, for example: |
| // `fn foo(&mut self, field_0: String)`. |
| string rust_sync_service_call_declaration(t_function* tfunc, bool self_is_mutable); |
| |
| // Return a string containing all the unpacked service call args given a service call function |
| // `t_function`. Only includes the arg names, each of which is prefixed with the optional prefix |
| // `field_prefix`, for example: `self.field_0`. |
| string rust_sync_service_call_invocation(t_function* tfunc, const string& field_prefix = ""); |
| |
| // Return a string containing all fields in the struct `tstruct` for use in a function declaration. |
| // Each field is followed by its type, for example: `field_0: String`. |
| string struct_to_declaration(t_struct* tstruct, t_rs_generator::e_struct_type struct_type); |
| |
| // Return a string containing all fields in the struct `tstruct` for use in a function call, |
| // for example: `field_0: String`. |
| string struct_to_invocation(t_struct* tstruct, const string& field_prefix = ""); |
| |
| // Write the documentation for a struct, service-call or other documentation-annotated element. |
| void render_rustdoc(t_doc* tdoc); |
| |
| // Return `true` if the true type of `ttype` is a thrift double, `false` otherwise. |
| bool is_double(t_type* ttype); |
| |
| // Return a string representing the rust type given a `t_type`. |
| string to_rust_type(t_type* ttype, bool ordered_float = true); |
| |
| // Return a string representing the `const` rust type given a `t_type` |
| string to_rust_const_type(t_type* ttype, bool ordered_float = true); |
| |
| // Return a string representing the rift `protocol::TType` given a `t_type`. |
| string to_rust_field_type_enum(t_type* ttype); |
| |
| // Return the default value to be used when initializing a struct field which has `OPT_IN_REQ_OUT` |
| // optionality. |
| string opt_in_req_out_value(t_type* ttype); |
| |
| // Return `true` if we can write a const of the form `pub const FOO: ...`. |
| bool can_generate_simple_const(t_type* ttype); |
| |
| // Return `true` if we cannot write a standard Rust constant (because the type needs some allocation). |
| bool can_generate_const_holder(t_type* ttype); |
| |
| // Return `true` if this type is a void, and should be represented by the rust `()` type. |
| bool is_void(t_type* ttype); |
| |
| t_field::e_req actual_field_req(t_field* tfield, t_rs_generator::e_struct_type struct_type); |
| |
| // Return `true` if this `t_field::e_req` is either `t_field::T_OPTIONAL` or `t_field::T_OPT_IN_REQ_OUT` |
| // and needs to be wrapped by an `Option<TYPE_NAME>`, `false` otherwise. |
| bool is_optional(t_field::e_req req); |
| |
| // Return `true` if the service call has arguments, `false` otherwise. |
| bool has_args(t_function* tfunc); |
| |
| // Return `true` if a service call has non-`()` arguments, `false` otherwise. |
| bool has_non_void_args(t_function* tfunc); |
| |
| // Return `pub ` (notice trailing whitespace!) if the struct should be public, `` (empty string) otherwise. |
| string visibility_qualifier(t_rs_generator::e_struct_type struct_type); |
| |
| // Returns the namespace prefix for a given Thrift service. If the type is defined in the presently-computed |
| // Thrift program, then an empty string is returned. |
| string rust_namespace(t_service* tservice); |
| |
| // Returns the namespace prefix for a given Thrift type. If the type is defined in the presently-computed |
| // Thrift program, then an empty string is returned. |
| string rust_namespace(t_type* ttype); |
| |
| // Returns the camel-cased name for a Rust struct type. Handles the case where `tstruct->get_name()` is |
| // a reserved word. |
| string rust_struct_name(t_struct* tstruct); |
| |
| // Returns the snake-cased name for a Rust field or local variable. Handles the case where |
| // `tfield->get_name()` is a reserved word. |
| string rust_field_name(t_field* tstruct); |
| |
| // Returns the camel-cased name for a Rust union type. Handles the case where `tstruct->get_name()` is |
| // a reserved word. |
| string rust_union_field_name(t_field* tstruct); |
| |
| // Converts any variable name into a 'safe' variant that does not clash with any Rust reserved keywords. |
| string rust_safe_name(const string& name); |
| |
| // Return `true` if the name is a reserved Rust keyword, `false` otherwise. |
| bool is_reserved(const string& name); |
| |
| // Return the name of the function that users will invoke to make outgoing service calls. |
| string service_call_client_function_name(t_function* tfunc); |
| |
| // Return the name of the function that users will have to implement to handle incoming service calls. |
| string service_call_handler_function_name(t_function* tfunc); |
| |
| // Return the name of the struct used to pack the arguments for the thrift service call. |
| string service_call_args_struct_name(t_function* tfunc); |
| |
| // Return the name of the struct used to pack the return value |
| // and user-defined exceptions for the thrift service call. |
| string service_call_result_struct_name(t_function* tfunc); |
| |
| string rust_sync_client_marker_trait_name(t_service* tservice); |
| |
| // Return the trait name for the sync service client given a `t_service`. |
| string rust_sync_client_trait_name(t_service* tservice); |
| |
| // Return the name for the sync service client struct given a `t_service`. |
| string rust_sync_client_impl_name(t_service* tservice); |
| |
| // Return the trait name that users will have to implement for the server half of a Thrift service. |
| string rust_sync_handler_trait_name(t_service* tservice); |
| |
| // Return the struct name for the server half of a Thrift service. |
| string rust_sync_processor_name(t_service* tservice); |
| |
| // Return the struct name for the struct that contains all the service-call implementations for |
| // the server half of a Thrift service. |
| string rust_sync_processor_impl_name(t_service *tservice); |
| |
| // Return the variant name for an enum variant |
| string rust_enum_variant_name(const string& name); |
| |
| // Properly uppercase names for use in Rust. |
| string rust_upper_case(const string& name); |
| |
| // Snake-case field, parameter and function names and make them Rust friendly. |
| string rust_snake_case(const string& name); |
| |
| // Camel-case type/variant names and make them Rust friendly. |
| string rust_camel_case(const string& name); |
| |
| // Replace all instances of `search_string` with `replace_string` in `target`. |
| void string_replace(string& target, const string& search_string, const string& replace_string); |
| |
| // Adjust field identifier to correctly handle unspecified field identifiers |
| // THRIFT-4953 |
| string rust_safe_field_id(int32_t id); |
| }; |
| |
| void t_rs_generator::init_generator() { |
| // make output directory for this thrift program |
| MKDIR(gen_dir_.c_str()); |
| |
| // create the file into which we're going to write the generated code |
| string f_gen_name = gen_dir_ + "/" + rust_snake_case(get_program()->get_name()) + ".rs"; |
| f_gen_.open(f_gen_name.c_str()); |
| |
| // header comment |
| f_gen_ << "// " << autogen_summary() << endl; |
| f_gen_ << "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING" << endl; |
| f_gen_ << endl; |
| |
| render_attributes_and_includes(); |
| } |
| |
| void t_rs_generator::render_attributes_and_includes() { |
| // turn off some compiler/clippy warnings |
| |
| // code always includes BTreeMap/BTreeSet/OrderedFloat |
| f_gen_ << "#![allow(unused_imports)]" << endl; |
| // code might not include imports from crates |
| f_gen_ << "#![allow(unused_extern_crates)]" << endl; |
| // constructors take *all* struct parameters, which can trigger the "too many arguments" warning |
| // some auto-gen'd types can be deeply nested. clippy recommends factoring them out which is hard to autogen |
| f_gen_ << "#![allow(clippy::too_many_arguments, clippy::type_complexity)]" << endl; |
| // prevent rustfmt from running against this file |
| // lines are too long, code is (thankfully!) not visual-indented, etc. |
| f_gen_ << "#![cfg_attr(rustfmt, rustfmt_skip)]" << endl; |
| f_gen_ << endl; |
| |
| // add standard includes |
| f_gen_ << "use std::cell::RefCell;" << endl; |
| f_gen_ << "use std::collections::{BTreeMap, BTreeSet};" << endl; |
| f_gen_ << "use std::convert::{From, TryFrom};" << endl; |
| f_gen_ << "use std::default::Default;" << endl; |
| f_gen_ << "use std::error::Error;" << endl; |
| f_gen_ << "use std::fmt;" << endl; |
| f_gen_ << "use std::fmt::{Display, Formatter};" << endl; |
| f_gen_ << "use std::rc::Rc;" << endl; |
| f_gen_ << endl; |
| f_gen_ << "use thrift::OrderedFloat;" << endl; |
| f_gen_ << "use thrift::{ApplicationError, ApplicationErrorKind, ProtocolError, ProtocolErrorKind, TThriftClient};" << endl; |
| f_gen_ << "use thrift::protocol::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TInputProtocol, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType};" << endl; |
| f_gen_ << "use thrift::protocol::field_id;" << endl; |
| f_gen_ << "use thrift::protocol::verify_expected_message_type;" << endl; |
| f_gen_ << "use thrift::protocol::verify_expected_sequence_number;" << endl; |
| f_gen_ << "use thrift::protocol::verify_expected_service_call;" << endl; |
| f_gen_ << "use thrift::protocol::verify_required_field_exists;" << endl; |
| f_gen_ << "use thrift::server::TProcessor;" << endl; |
| f_gen_ << endl; |
| |
| // add all the program includes |
| // NOTE: this is more involved than you would expect because of service extension |
| // Basically, I have to find the closure of all the services and include their modules at the top-level |
| |
| set<string> referenced_modules; |
| |
| // first, start by adding explicit thrift includes |
| const vector<t_program*> includes = get_program()->get_includes(); |
| vector<t_program*>::const_iterator includes_iter; |
| for(includes_iter = includes.begin(); includes_iter != includes.end(); ++includes_iter) { |
| referenced_modules.insert((*includes_iter)->get_name()); |
| } |
| |
| // next, recursively iterate through all the services and add the names of any programs they reference |
| const vector<t_service*> services = get_program()->get_services(); |
| vector<t_service*>::const_iterator service_iter; |
| for (service_iter = services.begin(); service_iter != services.end(); ++service_iter) { |
| compute_service_referenced_modules(*service_iter, referenced_modules); |
| } |
| |
| // finally, write all the "pub use..." declarations |
| if (!referenced_modules.empty()) { |
| set<string>::iterator module_iter; |
| for (module_iter = referenced_modules.begin(); module_iter != referenced_modules.end(); ++module_iter) { |
| f_gen_ << "use crate::" << rust_snake_case(*module_iter) << ";" << endl; |
| } |
| f_gen_ << endl; |
| } |
| } |
| |
| void t_rs_generator::compute_service_referenced_modules( |
| t_service *tservice, |
| set<string> &referenced_modules |
| ) { |
| t_service* extends = tservice->get_extends(); |
| if (extends) { |
| if (extends->get_program() != get_program()) { |
| referenced_modules.insert(extends->get_program()->get_name()); |
| } |
| compute_service_referenced_modules(extends, referenced_modules); |
| } |
| } |
| |
| void t_rs_generator::close_generator() { |
| f_gen_.close(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Consts |
| // |
| // NOTE: consider using macros to generate constants |
| // |
| //----------------------------------------------------------------------------- |
| |
| // This is worse than it should be because constants |
| // aren't (sensibly) limited to scalar types |
| void t_rs_generator::generate_const(t_const* tconst) { |
| string name = tconst->get_name(); |
| t_type* ttype = tconst->get_type(); |
| t_const_value* tvalue = tconst->get_value(); |
| |
| if (can_generate_simple_const(ttype)) { |
| render_const_value(name, ttype, tvalue); |
| } else if (can_generate_const_holder(ttype)) { |
| render_const_value_holder(name, ttype, tvalue); |
| } else { |
| throw "cannot generate const for " + name; |
| } |
| } |
| |
| void t_rs_generator::render_const_value(const string& name, t_type* ttype, t_const_value* tvalue) { |
| if (!can_generate_simple_const(ttype)) { |
| throw "cannot generate simple rust constant for " + ttype->get_name(); |
| } |
| |
| f_gen_ << "pub const " << rust_upper_case(name) << ": " << to_rust_const_type(ttype) << " = "; |
| render_const_value(ttype, tvalue, false); |
| f_gen_ << ";" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_const_value_holder(const string& name, t_type* ttype, t_const_value* tvalue) { |
| if (!can_generate_const_holder(ttype)) { |
| throw "cannot generate constant holder for " + ttype->get_name(); |
| } |
| |
| string holder_name("Const" + rust_camel_case(name)); |
| |
| f_gen_ << indent() << "pub struct " << holder_name << ";" << endl; |
| f_gen_ << indent() << "impl " << holder_name << " {" << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "pub fn const_value() -> " << to_rust_type(ttype) << " {" << endl; |
| indent_up(); |
| render_const_value(ttype, tvalue); |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_const_value(t_type* ttype, t_const_value* tvalue, bool is_owned) { |
| if (ttype->is_base_type()) { |
| t_base_type* tbase_type = (t_base_type*)ttype; |
| switch (tbase_type->get_base()) { |
| case t_base_type::TYPE_STRING: |
| if (tbase_type->is_binary()) { |
| if (is_owned) { |
| f_gen_ << "\"" << tvalue->get_string() << "\""<< ".to_owned().into_bytes()"; |
| } else { |
| f_gen_ << "b\"" << tvalue->get_string() << "\""; |
| } |
| } else { |
| f_gen_ << "\"" << tvalue->get_string() << "\""; |
| if (is_owned) { |
| f_gen_ << ".to_owned()"; |
| } |
| } |
| break; |
| case t_base_type::TYPE_BOOL: |
| f_gen_ << (tvalue->get_integer() ? "true" : "false"); |
| break; |
| case t_base_type::TYPE_I8: |
| case t_base_type::TYPE_I16: |
| case t_base_type::TYPE_I32: |
| case t_base_type::TYPE_I64: |
| f_gen_ << tvalue->get_integer(); |
| break; |
| case t_base_type::TYPE_DOUBLE: |
| f_gen_ << "OrderedFloat::from(" << tvalue->get_double() << "_f64)"; |
| break; |
| default: |
| throw "cannot generate const value for " + t_base_type::t_base_name(tbase_type->get_base()); |
| } |
| } else if (ttype->is_typedef()) { |
| render_const_value(get_true_type(ttype), tvalue); |
| } else if (ttype->is_enum()) { |
| f_gen_ << indent() << "{" << endl; |
| indent_up(); |
| f_gen_ |
| << indent() |
| << to_rust_type(ttype) |
| << "::try_from(" |
| << tvalue->get_integer() |
| << ").expect(\"expecting valid const value\")" |
| << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } else if (ttype->is_struct() || ttype->is_xception()) { |
| render_const_struct(ttype, tvalue); |
| } else if (ttype->is_container()) { |
| f_gen_ << indent() << "{" << endl; |
| indent_up(); |
| |
| if (ttype->is_list()) { |
| render_const_list(ttype, tvalue); |
| } else if (ttype->is_set()) { |
| render_const_set(ttype, tvalue); |
| } else if (ttype->is_map()) { |
| render_const_map(ttype, tvalue); |
| } else { |
| throw "cannot generate const container value for " + ttype->get_name(); |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } else { |
| throw "cannot generate const value for " + ttype->get_name(); |
| } |
| } |
| |
| void t_rs_generator::render_const_struct(t_type* ttype, t_const_value*) { |
| if (((t_struct*)ttype)->is_union()) { |
| f_gen_ << indent() << "{" << endl; |
| indent_up(); |
| f_gen_ << indent() << "unimplemented!()" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } else { |
| f_gen_ << indent() << "{" << endl; |
| indent_up(); |
| f_gen_ << indent() << "unimplemented!()" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| } |
| |
| void t_rs_generator::render_const_list(t_type* ttype, t_const_value* tvalue) { |
| t_type* elem_type = ((t_list*)ttype)->get_elem_type(); |
| f_gen_ << indent() << "let mut l: Vec<" << to_rust_type(elem_type) << "> = Vec::new();" << endl; |
| const vector<t_const_value*>& elems = tvalue->get_list(); |
| vector<t_const_value*>::const_iterator elem_iter; |
| for(elem_iter = elems.begin(); elem_iter != elems.end(); ++elem_iter) { |
| t_const_value* elem_value = (*elem_iter); |
| render_container_const_value("l.push", elem_type, elem_value); |
| } |
| f_gen_ << indent() << "l" << endl; |
| } |
| |
| void t_rs_generator::render_const_set(t_type* ttype, t_const_value* tvalue) { |
| t_type* elem_type = ((t_set*)ttype)->get_elem_type(); |
| f_gen_ << indent() << "let mut s: BTreeSet<" << to_rust_type(elem_type) << "> = BTreeSet::new();" << endl; |
| const vector<t_const_value*>& elems = tvalue->get_list(); |
| vector<t_const_value*>::const_iterator elem_iter; |
| for(elem_iter = elems.begin(); elem_iter != elems.end(); ++elem_iter) { |
| t_const_value* elem_value = (*elem_iter); |
| render_container_const_value("s.insert", elem_type, elem_value); |
| } |
| f_gen_ << indent() << "s" << endl; |
| } |
| |
| void t_rs_generator::render_const_map(t_type* ttype, t_const_value* tvalue) { |
| t_type* key_type = ((t_map*)ttype)->get_key_type(); |
| t_type* val_type = ((t_map*)ttype)->get_val_type(); |
| f_gen_ |
| << indent() |
| << "let mut m: BTreeMap<" |
| << to_rust_type(key_type) << ", " << to_rust_type(val_type) |
| << "> = BTreeMap::new();" |
| << endl; |
| const map<t_const_value*, t_const_value*, t_const_value::value_compare>& elems = tvalue->get_map(); |
| map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator elem_iter; |
| for (elem_iter = elems.begin(); elem_iter != elems.end(); ++elem_iter) { |
| t_const_value* key_value = elem_iter->first; |
| t_const_value* val_value = elem_iter->second; |
| if (get_true_type(key_type)->is_base_type()) { |
| f_gen_ << indent() << "let k = "; |
| render_const_value(key_type, key_value); |
| f_gen_ << ";" << endl; |
| } else { |
| f_gen_ << indent() << "let k = {" << endl; |
| indent_up(); |
| render_const_value(key_type, key_value); |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; |
| } |
| if (get_true_type(val_type)->is_base_type()) { |
| f_gen_ << indent() << "let v = "; |
| render_const_value(val_type, val_value); |
| f_gen_ << ";" << endl; |
| } else { |
| f_gen_ << indent() << "let v = {" << endl; |
| indent_up(); |
| render_const_value(val_type, val_value); |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; |
| } |
| f_gen_ << indent() << "m.insert(k, v);" << endl; |
| } |
| f_gen_ << indent() << "m" << endl; |
| } |
| |
| void t_rs_generator::render_container_const_value( |
| const string& insert_function, |
| t_type* ttype, |
| t_const_value* tvalue |
| ) { |
| if (get_true_type(ttype)->is_base_type()) { |
| f_gen_ << indent() << insert_function << "("; |
| render_const_value(ttype, tvalue); |
| f_gen_ << ");" << endl; |
| } else { |
| f_gen_ << indent() << insert_function << "(" << endl; |
| indent_up(); |
| render_const_value(ttype, tvalue); |
| indent_down(); |
| f_gen_ << indent() << ");" << endl; |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Typedefs |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::generate_typedef(t_typedef* ttypedef) { |
| std::string actual_type = to_rust_type(ttypedef->get_type()); |
| f_gen_ << "pub type " << rust_safe_name(ttypedef->get_symbolic()) << " = " << actual_type << ";" << endl; |
| f_gen_ << endl; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Enums |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::generate_enum(t_enum* tenum) { |
| string enum_name(rust_camel_case(tenum->get_name())); |
| render_enum_definition(tenum, enum_name); |
| render_enum_impl(enum_name); |
| render_enum_conversion(tenum, enum_name); |
| } |
| |
| void t_rs_generator::render_enum_definition(t_enum* tenum, const string& enum_name) { |
| render_rustdoc((t_doc*) tenum); |
| f_gen_ << "#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl; |
| f_gen_ << "pub enum " << enum_name << " {" << endl; |
| indent_up(); |
| |
| vector<t_enum_value*> constants = tenum->get_constants(); |
| vector<t_enum_value*>::iterator constants_iter; |
| for (constants_iter = constants.begin(); constants_iter != constants.end(); ++constants_iter) { |
| t_enum_value* val = (*constants_iter); |
| render_rustdoc((t_doc*) val); |
| f_gen_ |
| << indent() |
| << rust_enum_variant_name(val->get_name()) |
| << " = " |
| << val->get_value() |
| << "," |
| << endl; |
| } |
| |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_enum_impl(const string& enum_name) { |
| f_gen_ << "impl " << enum_name << " {" << endl; |
| indent_up(); |
| |
| // taking enum as 'self' here because Thrift enums |
| // are represented as Rust enums with integer values |
| // it's cheaper to copy the integer as opposed to |
| // taking a reference to the enum |
| f_gen_ |
| << indent() |
| << "pub fn write_to_out_protocol(self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" |
| << endl; |
| indent_up(); |
| f_gen_ << indent() << "o_prot.write_i32(self as i32)" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| f_gen_ |
| << indent() |
| << "pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << enum_name << "> {" |
| << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "let enum_value = i_prot.read_i32()?;" << endl; |
| f_gen_ << indent() << enum_name << "::try_from(enum_value)"; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_enum_conversion(t_enum* tenum, const string& enum_name) { |
| f_gen_ << "impl TryFrom<i32> for " << enum_name << " {" << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "type Error = thrift::Error;"; |
| |
| f_gen_ << indent() << "fn try_from(i: i32) -> Result<Self, Self::Error> {" << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "match i {" << endl; |
| indent_up(); |
| |
| vector<t_enum_value*> constants = tenum->get_constants(); |
| vector<t_enum_value*>::iterator constants_iter; |
| for (constants_iter = constants.begin(); constants_iter != constants.end(); ++constants_iter) { |
| t_enum_value* val = (*constants_iter); |
| f_gen_ |
| << indent() |
| << val->get_value() |
| << " => Ok(" << enum_name << "::" << rust_enum_variant_name(val->get_name()) << ")," |
| << endl; |
| } |
| f_gen_ << indent() << "_ => {" << endl; |
| indent_up(); |
| render_thrift_error( |
| "Protocol", |
| "ProtocolError", |
| "ProtocolErrorKind::InvalidData", |
| "format!(\"cannot convert enum constant {} to " + enum_name + "\", i)" |
| ); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Structs, Unions and Exceptions |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::generate_xception(t_struct* txception) { |
| render_struct(rust_struct_name(txception), txception, t_rs_generator::T_EXCEPTION); |
| } |
| |
| void t_rs_generator::generate_struct(t_struct* tstruct) { |
| if (tstruct->is_union()) { |
| render_union(tstruct); |
| } else if (tstruct->is_struct()) { |
| render_struct(rust_struct_name(tstruct), tstruct, t_rs_generator::T_REGULAR); |
| } else { |
| throw "cannot generate struct for exception"; |
| } |
| } |
| |
| void t_rs_generator::render_struct( |
| const string& struct_name, |
| t_struct* tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ) { |
| render_type_comment(struct_name); |
| render_struct_definition(struct_name, tstruct, struct_type); |
| render_struct_impl(struct_name, tstruct, struct_type); |
| if (struct_type == t_rs_generator::T_REGULAR || struct_type == t_rs_generator::T_EXCEPTION) { |
| render_struct_default_trait_impl(struct_name, tstruct); |
| } |
| if (struct_type == t_rs_generator::T_EXCEPTION) { |
| render_exception_struct_error_trait_impls(struct_name, tstruct); |
| } |
| } |
| |
| void t_rs_generator::render_struct_definition( |
| const string& struct_name, |
| t_struct* tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ) { |
| render_rustdoc((t_doc*) tstruct); |
| f_gen_ << "#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl; |
| f_gen_ << visibility_qualifier(struct_type) << "struct " << struct_name << " {" << endl; |
| |
| // render the members |
| vector<t_field*> members = tstruct->get_sorted_members(); |
| if (!members.empty()) { |
| indent_up(); |
| |
| vector<t_field*>::iterator members_iter; |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| t_field::e_req member_req = actual_field_req(member, struct_type); |
| |
| string rust_type = to_rust_type(member->get_type()); |
| rust_type = is_optional(member_req) ? "Option<" + rust_type + ">" : rust_type; |
| |
| render_rustdoc((t_doc*) member); |
| f_gen_ |
| << indent() |
| << visibility_qualifier(struct_type) |
| << rust_field_name(member) << ": " << rust_type << "," |
| << endl; |
| } |
| |
| indent_down(); |
| } |
| |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_exception_struct_error_trait_impls(const string& struct_name, t_struct* tstruct) { |
| // error::Error trait |
| f_gen_ << "impl Error for " << struct_name << " {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "fn description(&self) -> &str {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "\"" << "remote service threw " << tstruct->get_name() << "\"" << endl; // use *original* name |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| |
| // convert::From trait |
| f_gen_ << "impl From<" << struct_name << "> for thrift::Error {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "fn from(e: " << struct_name << ") -> Self {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "thrift::Error::User(Box::new(e))" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| |
| // fmt::Display trait |
| f_gen_ << "impl Display for " << struct_name << " {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "fn fmt(&self, f: &mut Formatter) -> fmt::Result {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "self.description().fmt(f)" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_struct_default_trait_impl(const string& struct_name, t_struct* tstruct) { |
| bool has_required_field = false; |
| |
| const vector<t_field*>& members = tstruct->get_sorted_members(); |
| vector<t_field*>::const_iterator members_iter; |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = *members_iter; |
| if (!is_optional(member->get_req())) { |
| has_required_field = true; |
| break; |
| } |
| } |
| |
| if (has_required_field) { |
| return; |
| } |
| |
| f_gen_ << "impl Default for " << struct_name << " {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "fn default() -> Self {" << endl; |
| indent_up(); |
| |
| if (members.empty()) { |
| f_gen_ << indent() << struct_name << "{}" << endl; |
| } else { |
| f_gen_ << indent() << struct_name << "{" << endl; |
| indent_up(); |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field *member = (*members_iter); |
| string member_name(rust_field_name(member)); |
| f_gen_ << indent() << member_name << ": " << opt_in_req_out_value(member->get_type()) << "," << endl; |
| } |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_struct_impl( |
| const string& struct_name, |
| t_struct* tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ) { |
| f_gen_ << "impl " << struct_name << " {" << endl; |
| indent_up(); |
| |
| if (struct_type == t_rs_generator::T_REGULAR || struct_type == t_rs_generator::T_EXCEPTION) { |
| render_struct_constructor(struct_name, tstruct, struct_type); |
| } |
| |
| render_struct_sync_read(struct_name, tstruct, struct_type); |
| render_struct_sync_write(tstruct, struct_type); |
| |
| if (struct_type == t_rs_generator::T_RESULT) { |
| render_result_struct_to_result_method(tstruct); |
| } |
| |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_struct_constructor( |
| const string& struct_name, |
| t_struct* tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ) { |
| const vector<t_field*>& members = tstruct->get_sorted_members(); |
| vector<t_field*>::const_iterator members_iter; |
| |
| // build the convenience type parameters that allows us to pass unwrapped values to a constructor and |
| // have them automatically converted into Option<value> |
| bool first_arg = true; |
| |
| ostringstream generic_type_parameters; |
| ostringstream generic_type_qualifiers; |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| t_field::e_req member_req = actual_field_req(member, struct_type); |
| |
| if (is_optional(member_req)) { |
| if (first_arg) { |
| first_arg = false; |
| } else { |
| generic_type_parameters << ", "; |
| generic_type_qualifiers << ", "; |
| } |
| generic_type_parameters << "F" << rust_safe_field_id(member->get_key()); |
| generic_type_qualifiers << "F" << rust_safe_field_id(member->get_key()) << ": Into<Option<" << to_rust_type(member->get_type()) << ">>"; |
| } |
| } |
| |
| string type_parameter_string = generic_type_parameters.str(); |
| if (type_parameter_string.length() != 0) { |
| type_parameter_string = "<" + type_parameter_string + ">"; |
| } |
| |
| string type_qualifier_string = generic_type_qualifiers.str(); |
| if (type_qualifier_string.length() != 0) { |
| type_qualifier_string = "where " + type_qualifier_string + " "; |
| } |
| |
| // now build the actual constructor arg list |
| // when we're building this list we have to use the type parameters in place of the actual type names |
| // if necessary |
| ostringstream args; |
| first_arg = true; |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| t_field::e_req member_req = actual_field_req(member, struct_type); |
| string member_name(rust_field_name(member)); |
| |
| if (first_arg) { |
| first_arg = false; |
| } else { |
| args << ", "; |
| } |
| |
| if (is_optional(member_req)) { |
| args << member_name << ": " << "F" << rust_safe_field_id(member->get_key()); |
| } else { |
| args << member_name << ": " << to_rust_type(member->get_type()); |
| } |
| } |
| |
| string arg_string = args.str(); |
| |
| string visibility(visibility_qualifier(struct_type)); |
| f_gen_ |
| << indent() |
| << visibility |
| << "fn new" |
| << type_parameter_string |
| << "(" |
| << arg_string |
| << ") -> " |
| << struct_name |
| << " " |
| << type_qualifier_string |
| << "{" |
| << endl; |
| indent_up(); |
| |
| if (members.empty()) { |
| f_gen_ << indent() << struct_name << " {}" << endl; |
| } else { |
| f_gen_ << indent() << struct_name << " {" << endl; |
| indent_up(); |
| |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| t_field::e_req member_req = actual_field_req(member, struct_type); |
| string member_name(rust_field_name(member)); |
| |
| if (is_optional(member_req)) { |
| f_gen_ << indent() << member_name << ": " << member_name << ".into()," << endl; |
| } else { |
| f_gen_ << indent() << member_name << "," << endl; |
| } |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_result_struct_to_result_method(t_struct* tstruct) { |
| // we don't use the rust struct name in this method, just the service call name |
| string service_call_name = tstruct->get_name(); |
| |
| // check that we actually have a result |
| size_t index = service_call_name.find(RESULT_STRUCT_SUFFIX, 0); |
| if (index == std::string::npos) { |
| throw "result struct " + service_call_name + " missing result suffix"; |
| } else { |
| service_call_name.replace(index, 6, ""); |
| } |
| |
| const vector<t_field*>& members = tstruct->get_sorted_members(); |
| vector<t_field*>::const_iterator members_iter; |
| |
| // find out what the call's expected return type was |
| string rust_return_type = "()"; |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| if (member->get_name() == SERVICE_RESULT_VARIABLE) { // don't have to check safe name here |
| rust_return_type = to_rust_type(member->get_type()); |
| break; |
| } |
| } |
| |
| // NOTE: ideally I would generate the branches and render them separately |
| // I tried this however, and the resulting code was harder to understand |
| // maintaining a rendered branch count (while a little ugly) got me the |
| // rendering I wanted with code that was reasonably understandable |
| |
| f_gen_ << indent() << "fn ok_or(self) -> thrift::Result<" << rust_return_type << "> {" << endl; |
| indent_up(); |
| |
| int rendered_branch_count = 0; |
| |
| // render the exception branches |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* tfield = (*members_iter); |
| if (tfield->get_name() != SERVICE_RESULT_VARIABLE) { // don't have to check safe name here |
| string field_name("self." + rust_field_name(tfield)); |
| string branch_statement = rendered_branch_count == 0 ? "if" : "} else if"; |
| |
| f_gen_ << indent() << branch_statement << " " << field_name << ".is_some() {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "Err(thrift::Error::User(Box::new(" << field_name << ".unwrap())))" << endl; |
| indent_down(); |
| |
| rendered_branch_count++; |
| } |
| } |
| |
| // render the return value branches |
| if (rust_return_type == "()") { |
| if (rendered_branch_count == 0) { |
| // we have the unit return and this service call has no user-defined |
| // exceptions. this means that we've a trivial return (happens with oneways) |
| f_gen_ << indent() << "Ok(())" << endl; |
| } else { |
| // we have the unit return, but there are user-defined exceptions |
| // if we've gotten this far then we have the default return (i.e. call successful) |
| f_gen_ << indent() << "} else {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "Ok(())" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| } else { |
| string branch_statement = rendered_branch_count == 0 ? "if" : "} else if"; |
| f_gen_ << indent() << branch_statement << " self." << SERVICE_RESULT_VARIABLE << ".is_some() {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "Ok(self." << SERVICE_RESULT_VARIABLE << ".unwrap())" << endl; |
| indent_down(); |
| f_gen_ << indent() << "} else {" << endl; |
| indent_up(); |
| // if we haven't found a valid return value *or* a user exception |
| // then we're in trouble; return a default error |
| render_thrift_error( |
| "Application", |
| "ApplicationError", |
| "ApplicationErrorKind::MissingResult", |
| "\"no result received for " + service_call_name + "\"" |
| ); |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_union(t_struct* tstruct) { |
| string union_name(rust_struct_name(tstruct)); |
| render_type_comment(union_name); |
| render_union_definition(union_name, tstruct); |
| render_union_impl(union_name, tstruct); |
| } |
| |
| void t_rs_generator::render_union_definition(const string& union_name, t_struct* tstruct) { |
| const vector<t_field*>& members = tstruct->get_sorted_members(); |
| if (members.empty()) { |
| throw "cannot generate rust enum with 0 members"; // may be valid thrift, but it's invalid rust |
| } |
| |
| f_gen_ << "#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl; |
| f_gen_ << "pub enum " << union_name << " {" << endl; |
| indent_up(); |
| |
| vector<t_field*>::const_iterator member_iter; |
| for(member_iter = members.begin(); member_iter != members.end(); ++member_iter) { |
| t_field* tfield = (*member_iter); |
| f_gen_ |
| << indent() |
| << rust_union_field_name(tfield) |
| << "(" << to_rust_type(tfield->get_type()) << ")," |
| << endl; |
| } |
| |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_union_impl(const string& union_name, t_struct* tstruct) { |
| f_gen_ << "impl " << union_name << " {" << endl; |
| indent_up(); |
| |
| render_union_sync_read(union_name, tstruct); |
| render_union_sync_write(union_name, tstruct); |
| |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Sync Struct Write |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::render_struct_sync_write( |
| t_struct *tstruct, |
| t_rs_generator::e_struct_type struct_type |
| ) { |
| f_gen_ |
| << indent() |
| << visibility_qualifier(struct_type) |
| << "fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" |
| << endl; |
| indent_up(); |
| |
| // write struct header to output protocol |
| // note: use the *original* struct name here |
| f_gen_ << indent() << "let struct_ident = TStructIdentifier::new(\"" + tstruct->get_name() + "\");" << endl; |
| f_gen_ << indent() << "o_prot.write_struct_begin(&struct_ident)?;" << endl; |
| |
| // write struct members to output protocol |
| vector<t_field*> members = tstruct->get_sorted_members(); |
| if (!members.empty()) { |
| vector<t_field*>::iterator members_iter; |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| t_field::e_req member_req = actual_field_req(member, struct_type); |
| string member_var("self." + rust_field_name(member)); |
| render_struct_field_sync_write(member_var, false, member, member_req); |
| } |
| } |
| |
| // write struct footer to output protocol |
| f_gen_ << indent() << "o_prot.write_field_stop()?;" << endl; |
| f_gen_ << indent() << "o_prot.write_struct_end()" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_union_sync_write(const string &union_name, t_struct *tstruct) { |
| f_gen_ |
| << indent() |
| << "pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" |
| << endl; |
| indent_up(); |
| |
| // write struct header to output protocol |
| // note: use the *original* struct name here |
| f_gen_ << indent() << "let struct_ident = TStructIdentifier::new(\"" + tstruct->get_name() + "\");" << endl; |
| f_gen_ << indent() << "o_prot.write_struct_begin(&struct_ident)?;" << endl; |
| |
| // write the enum field to the output protocol |
| vector<t_field*> members = tstruct->get_sorted_members(); |
| if (!members.empty()) { |
| f_gen_ << indent() << "match *self {" << endl; |
| indent_up(); |
| vector<t_field*>::iterator members_iter; |
| for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| t_field::e_req member_req = t_field::T_REQUIRED; |
| t_type* ttype = member->get_type(); |
| if (ttype->is_typedef()) { |
| // get the actual type of typedef |
| ttype = ((t_typedef*)ttype)->get_type(); |
| } |
| string match_var((ttype->is_base_type() && !ttype->is_string()) ? "f" : "ref f"); |
| f_gen_ |
| << indent() |
| << union_name << "::" << rust_union_field_name(member) |
| << "(" << match_var << ") => {" |
| << endl; |
| indent_up(); |
| render_struct_field_sync_write("f", true, member, member_req); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| } |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| // write struct footer to output protocol |
| f_gen_ << indent() << "o_prot.write_field_stop()?;" << endl; |
| f_gen_ << indent() << "o_prot.write_struct_end()" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_struct_field_sync_write( |
| const string &field_var, |
| bool field_var_is_ref, |
| t_field *tfield, |
| t_field::e_req req |
| ) { |
| t_type* field_type = tfield->get_type(); |
| t_type* actual_type = get_true_type(field_type); |
| |
| ostringstream field_stream; |
| field_stream |
| << "TFieldIdentifier::new(" |
| << "\"" << tfield->get_name() << "\"" << ", " // note: use *original* name |
| << to_rust_field_type_enum(field_type) << ", " |
| << tfield->get_key() << ")"; |
| string field_ident_string = field_stream.str(); |
| |
| if (is_optional(req)) { |
| string let_var((actual_type->is_base_type() && !actual_type->is_string()) ? "fld_var" : "ref fld_var"); |
| f_gen_ << indent() << "if let Some(" << let_var << ") = " << field_var << " {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "o_prot.write_field_begin(&" << field_ident_string << ")?;" << endl; |
| render_type_sync_write("fld_var", true, field_type); |
| f_gen_ << indent() << "o_prot.write_field_end()?" << endl; |
| indent_down(); |
| /* FIXME: rethink how I deal with OPT_IN_REQ_OUT |
| if (req == t_field::T_OPT_IN_REQ_OUT) { |
| f_gen_ << indent() << "let field_ident = " << field_ident_string << ";" << endl; |
| f_gen_ << indent() << "o_prot.write_field_begin(&field_ident)?;" << endl; |
| f_gen_ << indent() << "o_prot.write_field_end()?;" << endl; |
| }*/ |
| f_gen_ << indent() << "}" << endl; |
| } else { |
| f_gen_ << indent() << "o_prot.write_field_begin(&" << field_ident_string << ")?;" << endl; |
| render_type_sync_write(field_var, field_var_is_ref, tfield->get_type()); |
| f_gen_ << indent() << "o_prot.write_field_end()?;" << endl; |
| } |
| } |
| |
| void t_rs_generator::render_type_sync_write(const string &type_var, bool type_var_is_ref, t_type *ttype) { |
| if (ttype->is_base_type()) { |
| t_base_type* tbase_type = (t_base_type*)ttype; |
| switch (tbase_type->get_base()) { |
| case t_base_type::TYPE_VOID: |
| throw "cannot write field of type TYPE_VOID to output protocol"; |
| case t_base_type::TYPE_STRING: { |
| string ref(type_var_is_ref ? "" : "&"); |
| if (tbase_type->is_binary()) { |
| f_gen_ << indent() << "o_prot.write_bytes(" + ref + type_var + ")?;" << endl; |
| } else { |
| f_gen_ << indent() << "o_prot.write_string(" + ref + type_var + ")?;" << endl; |
| } |
| return; |
| } |
| case t_base_type::TYPE_BOOL: |
| f_gen_ << indent() << "o_prot.write_bool(" + type_var + ")?;" << endl; |
| return; |
| case t_base_type::TYPE_I8: |
| f_gen_ << indent() << "o_prot.write_i8(" + type_var + ")?;" << endl; |
| return; |
| case t_base_type::TYPE_I16: |
| f_gen_ << indent() << "o_prot.write_i16(" + type_var + ")?;" << endl; |
| return; |
| case t_base_type::TYPE_I32: |
| f_gen_ << indent() << "o_prot.write_i32(" + type_var + ")?;" << endl; |
| return; |
| case t_base_type::TYPE_I64: |
| f_gen_ << indent() << "o_prot.write_i64(" + type_var + ")?;" << endl; |
| return; |
| case t_base_type::TYPE_DOUBLE: |
| f_gen_ << indent() << "o_prot.write_double(" + type_var + ".into())?;" << endl; |
| return; |
| } |
| } else if (ttype->is_typedef()) { |
| t_typedef* ttypedef = (t_typedef*) ttype; |
| render_type_sync_write(type_var, type_var_is_ref, ttypedef->get_type()); |
| return; |
| } else if (ttype->is_enum() || ttype->is_struct() || ttype->is_xception()) { |
| f_gen_ << indent() << type_var + ".write_to_out_protocol(o_prot)?;" << endl; |
| return; |
| } else if (ttype->is_map()) { |
| render_map_sync_write(type_var, type_var_is_ref, (t_map *) ttype); |
| return; |
| } else if (ttype->is_set()) { |
| render_set_sync_write(type_var, type_var_is_ref, (t_set *) ttype); |
| return; |
| } else if (ttype->is_list()) { |
| render_list_sync_write(type_var, type_var_is_ref, (t_list *) ttype); |
| return; |
| } |
| |
| throw "cannot write unsupported type " + ttype->get_name(); |
| } |
| |
| void t_rs_generator::render_list_sync_write(const string &list_var, bool list_var_is_ref, t_list *tlist) { |
| t_type* elem_type = tlist->get_elem_type(); |
| |
| f_gen_ |
| << indent() |
| << "o_prot.write_list_begin(" |
| << "&TListIdentifier::new(" |
| << to_rust_field_type_enum(elem_type) << ", " |
| << list_var << ".len() as i32" << ")" |
| << ")?;" |
| << endl; |
| |
| string ref(list_var_is_ref ? "" : "&"); |
| f_gen_ << indent() << "for e in " << ref << list_var << " {" << endl; |
| indent_up(); |
| render_type_sync_write(string_container_write_variable(elem_type, "e"), true, elem_type); |
| f_gen_ << indent() << "o_prot.write_list_end()?;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_set_sync_write(const string &set_var, bool set_var_is_ref, t_set *tset) { |
| t_type* elem_type = tset->get_elem_type(); |
| |
| f_gen_ |
| << indent() |
| << "o_prot.write_set_begin(" |
| << "&TSetIdentifier::new(" |
| << to_rust_field_type_enum(elem_type) << ", " |
| << set_var << ".len() as i32" << ")" |
| << ")?;" |
| << endl; |
| |
| string ref(set_var_is_ref ? "" : "&"); |
| f_gen_ << indent() << "for e in " << ref << set_var << " {" << endl; |
| indent_up(); |
| render_type_sync_write(string_container_write_variable(elem_type, "e"), true, elem_type); |
| f_gen_ << indent() << "o_prot.write_set_end()?;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_map_sync_write(const string &map_var, bool map_var_is_ref, t_map *tmap) { |
| t_type* key_type = tmap->get_key_type(); |
| t_type* val_type = tmap->get_val_type(); |
| |
| f_gen_ |
| << indent() |
| << "o_prot.write_map_begin(" |
| << "&TMapIdentifier::new(" |
| << to_rust_field_type_enum(key_type) << ", " |
| << to_rust_field_type_enum(val_type) << ", " |
| << map_var << ".len() as i32)" |
| << ")?;" |
| << endl; |
| |
| string ref(map_var_is_ref ? "" : "&"); |
| f_gen_ << indent() << "for (k, v) in " << ref << map_var << " {" << endl; |
| indent_up(); |
| render_type_sync_write(string_container_write_variable(key_type, "k"), true, key_type); |
| render_type_sync_write(string_container_write_variable(val_type, "v"), true, val_type); |
| f_gen_ << indent() << "o_prot.write_map_end()?;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| string t_rs_generator::string_container_write_variable(t_type* ttype, const string& base_var) { |
| bool type_needs_deref = needs_deref_on_container_write(ttype); |
| bool type_is_double = is_double(ttype); |
| |
| string write_variable; |
| |
| if (type_is_double && type_needs_deref) { |
| write_variable = "(*" + base_var + ")"; |
| } else if (type_needs_deref) { |
| write_variable = "*" + base_var; |
| } else { |
| write_variable = base_var; |
| } |
| |
| return write_variable; |
| } |
| |
| bool t_rs_generator::needs_deref_on_container_write(t_type* ttype) { |
| ttype = get_true_type(ttype); |
| return ttype->is_base_type() && !ttype->is_string(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Sync Struct Read |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::render_struct_sync_read( |
| const string &struct_name, |
| t_struct *tstruct, t_rs_generator::e_struct_type struct_type |
| ) { |
| f_gen_ |
| << indent() |
| << visibility_qualifier(struct_type) |
| << "fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << struct_name << "> {" |
| << endl; |
| |
| indent_up(); |
| |
| f_gen_ << indent() << "i_prot.read_struct_begin()?;" << endl; |
| |
| // create temporary variables: one for each field in the struct |
| const vector<t_field*> members = tstruct->get_sorted_members(); |
| vector<t_field*>::const_iterator members_iter; |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| t_field::e_req member_req = actual_field_req(member, struct_type); |
| |
| f_gen_ |
| << indent() |
| << "let mut " << struct_field_read_temp_variable(member) |
| << ": Option<" << to_rust_type(member->get_type()) << "> = "; |
| if (member_req == t_field::T_OPT_IN_REQ_OUT) { |
| f_gen_ << opt_in_req_out_value(member->get_type()) << ";"; |
| } else { |
| f_gen_ << "None;"; |
| } |
| f_gen_ << endl; |
| } |
| |
| // now loop through the fields we've received |
| f_gen_ << indent() << "loop {" << endl; // start loop |
| indent_up(); |
| |
| // break out if you've found the Stop field |
| f_gen_ << indent() << "let field_ident = i_prot.read_field_begin()?;" << endl; |
| f_gen_ << indent() << "if field_ident.field_type == TType::Stop {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "break;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| // now read all the fields found |
| f_gen_ << indent() << "let field_id = field_id(&field_ident)?;" << endl; |
| f_gen_ << indent() << "match field_id {" << endl; // start match |
| indent_up(); |
| |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* tfield = (*members_iter); |
| f_gen_ << indent() << rust_safe_field_id(tfield->get_key()) << " => {" << endl; |
| indent_up(); |
| render_type_sync_read("val", tfield->get_type()); |
| f_gen_ << indent() << struct_field_read_temp_variable(tfield) << " = Some(val);" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| } |
| |
| // default case (skip fields) |
| f_gen_ << indent() << "_ => {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "i_prot.skip(field_ident.field_type)?;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; // finish match |
| f_gen_ << indent() << "i_prot.read_field_end()?;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; // finish loop |
| f_gen_ << indent() << "i_prot.read_struct_end()?;" << endl; // read message footer from the wire |
| |
| // verify that all required fields exist |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* tfield = (*members_iter); |
| t_field::e_req req = actual_field_req(tfield, struct_type); |
| if (!is_optional(req)) { |
| f_gen_ |
| << indent() |
| << "verify_required_field_exists(" |
| << "\"" << struct_name << "." << rust_field_name(tfield) << "\"" |
| << ", " |
| << "&" << struct_field_read_temp_variable(tfield) |
| << ")?;" << endl; |
| } |
| } |
| |
| // construct the struct |
| if (members.size() == 0) { |
| f_gen_ << indent() << "let ret = " << struct_name << " {};" << endl; |
| } else { |
| f_gen_ << indent() << "let ret = " << struct_name << " {" << endl; |
| indent_up(); |
| |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* tfield = (*members_iter); |
| t_field::e_req req = actual_field_req(tfield, struct_type); |
| string field_name(rust_field_name(tfield)); |
| string field_key = struct_field_read_temp_variable(tfield); |
| if (is_optional(req)) { |
| f_gen_ << indent() << field_name << ": " << field_key << "," << endl; |
| } else { |
| f_gen_ |
| << indent() |
| << field_name |
| << ": " |
| << field_key |
| << ".expect(\"auto-generated code should have checked for presence of required fields\")" |
| << "," |
| << endl; |
| } |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; |
| } |
| |
| // return the constructed value |
| f_gen_ << indent() << "Ok(ret)" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_union_sync_read(const string &union_name, t_struct *tstruct) { |
| f_gen_ |
| << indent() |
| << "pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << union_name << "> {" |
| << endl; |
| indent_up(); |
| |
| // create temporary variables to hold the |
| // completed union as well as a count of fields read |
| f_gen_ << indent() << "let mut ret: Option<" << union_name << "> = None;" << endl; |
| f_gen_ << indent() << "let mut received_field_count = 0;" << endl; |
| |
| // read the struct preamble |
| f_gen_ << indent() << "i_prot.read_struct_begin()?;" << endl; |
| |
| // now loop through the fields we've received |
| f_gen_ << indent() << "loop {" << endl; // start loop |
| indent_up(); |
| |
| // break out if you've found the Stop field |
| f_gen_ << indent() << "let field_ident = i_prot.read_field_begin()?;" << endl; |
| f_gen_ << indent() << "if field_ident.field_type == TType::Stop {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "break;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| // now read all the fields found |
| f_gen_ << indent() << "let field_id = field_id(&field_ident)?;" << endl; |
| f_gen_ << indent() << "match field_id {" << endl; // start match |
| indent_up(); |
| |
| const vector<t_field*> members = tstruct->get_sorted_members(); |
| vector<t_field*>::const_iterator members_iter; |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| f_gen_ << indent() << rust_safe_field_id(member->get_key()) << " => {" << endl; |
| indent_up(); |
| render_type_sync_read("val", member->get_type()); |
| f_gen_ << indent() << "if ret.is_none() {" << endl; |
| indent_up(); |
| f_gen_ |
| << indent() |
| << "ret = Some(" << union_name << "::" << rust_union_field_name(member) << "(val));" |
| << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << indent() << "received_field_count += 1;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| } |
| |
| // default case (skip fields) |
| f_gen_ << indent() << "_ => {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "i_prot.skip(field_ident.field_type)?;" << endl; |
| f_gen_ << indent() << "received_field_count += 1;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; // finish match |
| f_gen_ << indent() << "i_prot.read_field_end()?;" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; // finish loop |
| f_gen_ << indent() << "i_prot.read_struct_end()?;" << endl; // finish reading message from wire |
| |
| // return the value or an error |
| f_gen_ << indent() << "if received_field_count == 0 {" << endl; |
| indent_up(); |
| render_thrift_error( |
| "Protocol", |
| "ProtocolError", |
| "ProtocolErrorKind::InvalidData", |
| "\"received empty union from remote " + union_name + "\"" |
| ); |
| indent_down(); |
| f_gen_ << indent() << "} else if received_field_count > 1 {" << endl; |
| indent_up(); |
| render_thrift_error( |
| "Protocol", |
| "ProtocolError", |
| "ProtocolErrorKind::InvalidData", |
| "\"received multiple fields for union from remote " + union_name + "\"" |
| ); |
| indent_down(); |
| f_gen_ << indent() << "} else {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "Ok(ret.expect(\"return value should have been constructed\"))" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| // Construct the rust representation of all supported types from the wire. |
| void t_rs_generator::render_type_sync_read(const string &type_var, t_type *ttype, bool is_boxed) { |
| if (ttype->is_base_type()) { |
| t_base_type* tbase_type = (t_base_type*)ttype; |
| switch (tbase_type->get_base()) { |
| case t_base_type::TYPE_VOID: |
| throw "cannot read field of type TYPE_VOID from input protocol"; |
| case t_base_type::TYPE_STRING: |
| if (tbase_type->is_binary()) { |
| f_gen_ << indent() << "let " << type_var << " = i_prot.read_bytes()?;" << endl; |
| } else { |
| f_gen_ << indent() << "let " << type_var << " = i_prot.read_string()?;" << endl; |
| } |
| return; |
| case t_base_type::TYPE_BOOL: |
| f_gen_ << indent() << "let " << type_var << " = i_prot.read_bool()?;" << endl; |
| return; |
| case t_base_type::TYPE_I8: |
| f_gen_ << indent() << "let " << type_var << " = i_prot.read_i8()?;" << endl; |
| return; |
| case t_base_type::TYPE_I16: |
| f_gen_ << indent() << "let " << type_var << " = i_prot.read_i16()?;" << endl; |
| return; |
| case t_base_type::TYPE_I32: |
| f_gen_ << indent() << "let " << type_var << " = i_prot.read_i32()?;" << endl; |
| return; |
| case t_base_type::TYPE_I64: |
| f_gen_ << indent() << "let " << type_var << " = i_prot.read_i64()?;" << endl; |
| return; |
| case t_base_type::TYPE_DOUBLE: |
| f_gen_ << indent() << "let " << type_var << " = OrderedFloat::from(i_prot.read_double()?);" << endl; |
| return; |
| } |
| } else if (ttype->is_typedef()) { |
| // FIXME: not a fan of separate `is_boxed` parameter |
| // This is problematic because it's an optional parameter, and only comes |
| // into play once. The core issue is that I lose an important piece of type |
| // information (whether the type is a fwd ref) by unwrapping the typedef'd |
| // type and making the recursive call using it. I can't modify or wrap the |
| // generated string after the fact because it's written directly into the file, |
| // so I have to pass this parameter along. Going with this approach because it |
| // seems like the lowest-cost option to easily support recursive types. |
| t_typedef* ttypedef = (t_typedef*)ttype; |
| render_type_sync_read(type_var, ttypedef->get_type(), ttypedef->is_forward_typedef()); |
| return; |
| } else if (ttype->is_enum() || ttype->is_struct() || ttype->is_xception()) { |
| string read_call(to_rust_type(ttype) + "::read_from_in_protocol(i_prot)?"); |
| read_call = is_boxed ? "Box::new(" + read_call + ")" : read_call; |
| f_gen_ |
| << indent() |
| << "let " << type_var << " = " << read_call << ";" |
| << endl; |
| return; |
| } else if (ttype->is_map()) { |
| render_map_sync_read((t_map *) ttype, type_var); |
| return; |
| } else if (ttype->is_set()) { |
| render_set_sync_read((t_set *) ttype, type_var); |
| return; |
| } else if (ttype->is_list()) { |
| render_list_sync_read((t_list *) ttype, type_var); |
| return; |
| } |
| |
| throw "cannot read unsupported type " + ttype->get_name(); |
| } |
| |
| // Construct the rust representation of a list from the wire. |
| void t_rs_generator::render_list_sync_read(t_list *tlist, const string &list_var) { |
| t_type* elem_type = tlist->get_elem_type(); |
| |
| f_gen_ << indent() << "let list_ident = i_prot.read_list_begin()?;" << endl; |
| f_gen_ |
| << indent() |
| << "let mut " << list_var << ": " << to_rust_type((t_type*) tlist) |
| << " = Vec::with_capacity(list_ident.size as usize);" |
| << endl; |
| f_gen_ << indent() << "for _ in 0..list_ident.size {" << endl; |
| |
| indent_up(); |
| |
| string list_elem_var = tmp("list_elem_"); |
| render_type_sync_read(list_elem_var, elem_type); |
| f_gen_ << indent() << list_var << ".push(" << list_elem_var << ");" << endl; |
| |
| indent_down(); |
| |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << indent() << "i_prot.read_list_end()?;" << endl; |
| } |
| |
| // Construct the rust representation of a set from the wire. |
| void t_rs_generator::render_set_sync_read(t_set *tset, const string &set_var) { |
| t_type* elem_type = tset->get_elem_type(); |
| |
| f_gen_ << indent() << "let set_ident = i_prot.read_set_begin()?;" << endl; |
| f_gen_ |
| << indent() |
| << "let mut " << set_var << ": " << to_rust_type((t_type*) tset) |
| << " = BTreeSet::new();" |
| << endl; |
| f_gen_ << indent() << "for _ in 0..set_ident.size {" << endl; |
| |
| indent_up(); |
| |
| string set_elem_var = tmp("set_elem_"); |
| render_type_sync_read(set_elem_var, elem_type); |
| f_gen_ << indent() << set_var << ".insert(" << set_elem_var << ");" << endl; |
| |
| indent_down(); |
| |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << indent() << "i_prot.read_set_end()?;" << endl; |
| } |
| |
| // Construct the rust representation of a map from the wire. |
| void t_rs_generator::render_map_sync_read(t_map *tmap, const string &map_var) { |
| t_type* key_type = tmap->get_key_type(); |
| t_type* val_type = tmap->get_val_type(); |
| |
| f_gen_ << indent() << "let map_ident = i_prot.read_map_begin()?;" << endl; |
| f_gen_ |
| << indent() |
| << "let mut " << map_var << ": " << to_rust_type((t_type*) tmap) |
| << " = BTreeMap::new();" |
| << endl; |
| f_gen_ << indent() << "for _ in 0..map_ident.size {" << endl; |
| |
| indent_up(); |
| |
| string key_elem_var = tmp("map_key_"); |
| render_type_sync_read(key_elem_var, key_type); |
| string val_elem_var = tmp("map_val_"); |
| render_type_sync_read(val_elem_var, val_type); |
| f_gen_ << indent() << map_var << ".insert(" << key_elem_var << ", " << val_elem_var << ");" << endl; |
| |
| indent_down(); |
| |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << indent() << "i_prot.read_map_end()?;" << endl; |
| } |
| |
| string t_rs_generator::struct_field_read_temp_variable(t_field* tfield) { |
| std::ostringstream foss; |
| foss << "f_" << rust_safe_field_id(tfield->get_key()); |
| return foss.str(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Sync Client |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::generate_service(t_service* tservice) { |
| render_sync_client(tservice); |
| render_sync_processor(tservice); |
| render_service_call_structs(tservice); |
| } |
| |
| void t_rs_generator::render_service_call_structs(t_service* tservice) { |
| const std::vector<t_function*> functions = tservice->get_functions(); |
| std::vector<t_function*>::const_iterator func_iter; |
| |
| // thrift args for service calls are packed |
| // into a struct that's transmitted over the wire, so |
| // generate structs for those too |
| // |
| // thrift returns are *also* packed into a struct |
| // that's passed over the wire, so, generate the struct |
| // for that too. Note that this result struct *also* |
| // contains the exceptions as well |
| for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) { |
| t_function* tfunc = (*func_iter); |
| render_service_call_args_struct(tfunc); |
| if (!tfunc->is_oneway()) { |
| render_service_call_result_value_struct(tfunc); |
| } |
| } |
| } |
| |
| void t_rs_generator::render_sync_client(t_service* tservice) { |
| string client_impl_name(rust_sync_client_impl_name(tservice)); |
| |
| render_type_comment(tservice->get_name() + " service client"); // note: use *original* name |
| render_sync_client_trait(tservice); |
| render_sync_client_marker_trait(tservice); |
| render_sync_client_definition_and_impl(client_impl_name); |
| render_sync_client_tthriftclient_impl(client_impl_name); |
| render_sync_client_marker_trait_impls(tservice, client_impl_name); f_gen_ << endl; |
| render_sync_client_process_impl(tservice); |
| } |
| |
| void t_rs_generator::render_sync_client_trait(t_service *tservice) { |
| string extension = ""; |
| if (tservice->get_extends()) { |
| t_service* extends = tservice->get_extends(); |
| extension = " : " + rust_namespace(extends) + rust_sync_client_trait_name(extends); |
| } |
| |
| render_rustdoc((t_doc*) tservice); |
| f_gen_ << "pub trait " << rust_sync_client_trait_name(tservice) << extension << " {" << endl; |
| indent_up(); |
| |
| const std::vector<t_function*> functions = tservice->get_functions(); |
| std::vector<t_function*>::const_iterator func_iter; |
| for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) { |
| t_function* tfunc = (*func_iter); |
| string func_name = service_call_client_function_name(tfunc); |
| string func_args = rust_sync_service_call_declaration(tfunc, true); |
| string func_return = to_rust_type(tfunc->get_returntype()); |
| render_rustdoc((t_doc*) tfunc); |
| f_gen_ << indent() << "fn " << func_name << func_args << " -> thrift::Result<" << func_return << ">;" << endl; |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_sync_client_marker_trait(t_service *tservice) { |
| f_gen_ << indent() << "pub trait " << rust_sync_client_marker_trait_name(tservice) << " {}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_sync_client_marker_trait_impls(t_service *tservice, const string &impl_struct_name) { |
| f_gen_ |
| << indent() |
| << "impl " |
| << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " " |
| << rust_namespace(tservice) << rust_sync_client_marker_trait_name(tservice) |
| << " for " |
| << impl_struct_name << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " " |
| << SYNC_CLIENT_GENERIC_BOUNDS |
| << " {}" |
| << endl; |
| |
| t_service* extends = tservice->get_extends(); |
| if (extends) { |
| render_sync_client_marker_trait_impls(extends, impl_struct_name); |
| } |
| } |
| |
| void t_rs_generator::render_sync_client_definition_and_impl(const string& client_impl_name) { |
| |
| // render the definition for the client struct |
| f_gen_ |
| << "pub struct " |
| << client_impl_name |
| << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " " |
| << SYNC_CLIENT_GENERIC_BOUNDS |
| << " {" |
| << endl; |
| indent_up(); |
| f_gen_ << indent() << "_i_prot: IP," << endl; |
| f_gen_ << indent() << "_o_prot: OP," << endl; |
| f_gen_ << indent() << "_sequence_number: i32," << endl; |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| |
| // render the struct implementation |
| // this includes the new() function as well as the helper send/recv methods for each service call |
| f_gen_ |
| << "impl " |
| << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " " |
| << client_impl_name |
| << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " " |
| << SYNC_CLIENT_GENERIC_BOUNDS |
| << " {" |
| << endl; |
| indent_up(); |
| render_sync_client_lifecycle_functions(client_impl_name); |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_sync_client_lifecycle_functions(const string& client_struct) { |
| f_gen_ |
| << indent() |
| << "pub fn new(input_protocol: IP, output_protocol: OP) -> " |
| << client_struct |
| << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " {" |
| << endl; |
| indent_up(); |
| |
| f_gen_ |
| << indent() |
| << client_struct |
| << " { _i_prot: input_protocol, _o_prot: output_protocol, _sequence_number: 0 }" |
| << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_sync_client_tthriftclient_impl(const string &client_impl_name) { |
| f_gen_ |
| << indent() |
| << "impl " |
| << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " TThriftClient for " |
| << client_impl_name |
| << SYNC_CLIENT_GENERIC_BOUND_VARS |
| << " " |
| << SYNC_CLIENT_GENERIC_BOUNDS |
| << " {" << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "fn i_prot_mut(&mut self) -> &mut dyn TInputProtocol { &mut self._i_prot }" << endl; |
| f_gen_ << indent() << "fn o_prot_mut(&mut self) -> &mut dyn TOutputProtocol { &mut self._o_prot }" << endl; |
| f_gen_ << indent() << "fn sequence_number(&self) -> i32 { self._sequence_number }" << endl; |
| f_gen_ |
| << indent() |
| << "fn increment_sequence_number(&mut self) -> i32 { self._sequence_number += 1; self._sequence_number }" |
| << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_sync_client_process_impl(t_service* tservice) { |
| string marker_extension = "" + sync_client_marker_traits_for_extension(tservice); |
| |
| f_gen_ |
| << "impl <C: TThriftClient + " << rust_sync_client_marker_trait_name(tservice) << marker_extension << "> " |
| << rust_sync_client_trait_name(tservice) |
| << " for C {" << endl; |
| indent_up(); |
| |
| const std::vector<t_function*> functions = tservice->get_functions(); |
| std::vector<t_function*>::const_iterator func_iter; |
| for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) { |
| t_function* func = (*func_iter); |
| render_sync_send_recv_wrapper(func); |
| } |
| |
| indent_down(); |
| f_gen_ << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| string t_rs_generator::sync_client_marker_traits_for_extension(t_service *tservice) { |
| string marker_extension; |
| |
| t_service* extends = tservice->get_extends(); |
| if (extends) { |
| marker_extension = " + " + rust_namespace(extends) + rust_sync_client_marker_trait_name(extends); |
| marker_extension = marker_extension + sync_client_marker_traits_for_extension(extends); |
| } |
| |
| return marker_extension; |
| } |
| |
| void t_rs_generator::render_sync_send_recv_wrapper(t_function* tfunc) { |
| string func_name = service_call_client_function_name(tfunc); |
| string func_decl_args = rust_sync_service_call_declaration(tfunc, true); |
| string func_call_args = rust_sync_service_call_invocation(tfunc); |
| string func_return = to_rust_type(tfunc->get_returntype()); |
| |
| f_gen_ |
| << indent() |
| << "fn " << func_name << func_decl_args << " -> thrift::Result<" << func_return |
| << "> {" |
| << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "(" << endl; |
| indent_up(); |
| render_sync_send(tfunc); |
| indent_down(); |
| f_gen_ << indent() << ")?;" << endl; |
| if (tfunc->is_oneway()) { |
| f_gen_ << indent() << "Ok(())" << endl; |
| } else { |
| render_sync_recv(tfunc); |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_sync_send(t_function* tfunc) { |
| f_gen_ << indent() << "{" << endl; |
| indent_up(); |
| |
| // increment the sequence number and generate the call header |
| string message_type = tfunc->is_oneway() ? "TMessageType::OneWay" : "TMessageType::Call"; |
| f_gen_ << indent() << "self.increment_sequence_number();" << endl; |
| f_gen_ |
| << indent() |
| << "let message_ident = " |
| << "TMessageIdentifier::new(\"" << tfunc->get_name() << "\", " // note: use *original* name |
| << message_type << ", " |
| << "self.sequence_number());" |
| << endl; |
| // pack the arguments into the containing struct that we'll write out over the wire |
| // note that this struct is generated even if we have 0 args |
| ostringstream struct_definition; |
| vector<t_field*> members = tfunc->get_arglist()->get_sorted_members(); |
| vector<t_field*>::iterator members_iter; |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* member = (*members_iter); |
| string member_name(rust_field_name(member)); |
| struct_definition << member_name << ", "; |
| } |
| string struct_fields = struct_definition.str(); |
| if (struct_fields.size() > 0) { |
| struct_fields = struct_fields.substr(0, struct_fields.size() - 2); // strip trailing comma |
| } |
| f_gen_ |
| << indent() |
| << "let call_args = " |
| << service_call_args_struct_name(tfunc) |
| << " { " |
| << struct_fields |
| << " };" |
| << endl; |
| // write everything over the wire |
| f_gen_ << indent() << "self.o_prot_mut().write_message_begin(&message_ident)?;" << endl; |
| f_gen_ << indent() << "call_args.write_to_out_protocol(self.o_prot_mut())?;" << endl; // written even if we have 0 args |
| f_gen_ << indent() << "self.o_prot_mut().write_message_end()?;" << endl; |
| f_gen_ << indent() << "self.o_prot_mut().flush()" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_sync_recv(t_function* tfunc) { |
| f_gen_ << indent() << "{" << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "let message_ident = self.i_prot_mut().read_message_begin()?;" << endl; |
| f_gen_ << indent() << "verify_expected_sequence_number(self.sequence_number(), message_ident.sequence_number)?;" << endl; |
| f_gen_ << indent() << "verify_expected_service_call(\"" << tfunc->get_name() <<"\", &message_ident.name)?;" << endl; // note: use *original* name |
| // FIXME: replace with a "try" block |
| f_gen_ << indent() << "if message_ident.message_type == TMessageType::Exception {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "let remote_error = thrift::Error::read_application_error_from_in_protocol(self.i_prot_mut())?;" << endl; |
| f_gen_ << indent() << "self.i_prot_mut().read_message_end()?;" << endl; |
| f_gen_ << indent() << "return Err(thrift::Error::Application(remote_error))" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << indent() << "verify_expected_message_type(TMessageType::Reply, message_ident.message_type)?;" << endl; |
| f_gen_ << indent() << "let result = " << service_call_result_struct_name(tfunc) << "::read_from_in_protocol(self.i_prot_mut())?;" << endl; |
| f_gen_ << indent() << "self.i_prot_mut().read_message_end()?;" << endl; |
| f_gen_ << indent() << "result.ok_or()" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| string t_rs_generator::rust_sync_service_call_declaration(t_function* tfunc, bool self_is_mutable) { |
| ostringstream func_args; |
| |
| if (self_is_mutable) { |
| func_args << "(&mut self"; |
| } else { |
| func_args << "(&self"; |
| } |
| |
| if (has_args(tfunc)) { |
| func_args << ", "; // put comma after "self" |
| func_args << struct_to_declaration(tfunc->get_arglist(), T_ARGS); |
| } |
| |
| func_args << ")"; |
| return func_args.str(); |
| } |
| |
| string t_rs_generator::rust_sync_service_call_invocation(t_function* tfunc, const string& field_prefix) { |
| ostringstream func_args; |
| func_args << "("; |
| |
| if (has_args(tfunc)) { |
| func_args << struct_to_invocation(tfunc->get_arglist(), field_prefix); |
| } |
| |
| func_args << ")"; |
| return func_args.str(); |
| } |
| |
| string t_rs_generator::struct_to_declaration(t_struct* tstruct, t_rs_generator::e_struct_type struct_type) { |
| ostringstream args; |
| |
| bool first_arg = true; |
| std::vector<t_field*> fields = tstruct->get_sorted_members(); |
| std::vector<t_field*>::iterator field_iter; |
| for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) { |
| t_field* tfield = (*field_iter); |
| t_field::e_req field_req = actual_field_req(tfield, struct_type); |
| string rust_type = to_rust_type(tfield->get_type()); |
| rust_type = is_optional(field_req) ? "Option<" + rust_type + ">" : rust_type; |
| |
| if (first_arg) { |
| first_arg = false; |
| } else { |
| args << ", "; |
| } |
| |
| args << rust_field_name(tfield) << ": " << rust_type; |
| } |
| |
| return args.str(); |
| } |
| |
| string t_rs_generator::struct_to_invocation(t_struct* tstruct, const string& field_prefix) { |
| ostringstream args; |
| |
| bool first_arg = true; |
| std::vector<t_field*> fields = tstruct->get_sorted_members(); |
| std::vector<t_field*>::iterator field_iter; |
| for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) { |
| t_field* tfield = (*field_iter); |
| |
| if (first_arg) { |
| first_arg = false; |
| } else { |
| args << ", "; |
| } |
| |
| args << field_prefix << rust_field_name(tfield); |
| } |
| |
| return args.str(); |
| } |
| |
| void t_rs_generator::render_service_call_args_struct(t_function* tfunc) { |
| string args_struct_name(service_call_args_struct_name(tfunc)); |
| render_struct(args_struct_name, tfunc->get_arglist(), t_rs_generator::T_ARGS); |
| } |
| |
| void t_rs_generator::render_service_call_result_value_struct(t_function* tfunc) { |
| string result_struct_name = service_call_result_struct_name(tfunc); |
| t_struct result(program_, result_struct_name); |
| |
| t_field return_value(tfunc->get_returntype(), SERVICE_RESULT_VARIABLE, 0); |
| return_value.set_req(t_field::T_OPTIONAL); |
| if (!tfunc->get_returntype()->is_void()) { |
| result.append(&return_value); |
| } |
| |
| t_struct* exceptions = tfunc->get_xceptions(); |
| const vector<t_field*>& exception_types = exceptions->get_members(); |
| vector<t_field*>::const_iterator exception_iter; |
| for(exception_iter = exception_types.begin(); exception_iter != exception_types.end(); ++exception_iter) { |
| t_field* exception_type = *exception_iter; |
| exception_type->set_req(t_field::T_OPTIONAL); |
| result.append(exception_type); |
| } |
| |
| render_struct(result_struct_name, &result, t_rs_generator::T_RESULT); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Sync Processor |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::render_sync_processor(t_service *tservice) { |
| render_type_comment(tservice->get_name() + " service processor"); // note: use *original* name |
| render_sync_handler_trait(tservice); |
| render_sync_processor_definition_and_impl(tservice); |
| } |
| |
| void t_rs_generator::render_sync_handler_trait(t_service *tservice) { |
| string extension = ""; |
| if (tservice->get_extends() != nullptr) { |
| t_service* extends = tservice->get_extends(); |
| extension = " : " + rust_namespace(extends) + rust_sync_handler_trait_name(extends); |
| } |
| |
| const std::vector<t_function*> functions = tservice->get_functions(); |
| std::vector<t_function*>::const_iterator func_iter; |
| |
| render_rustdoc((t_doc*) tservice); |
| f_gen_ << "pub trait " << rust_sync_handler_trait_name(tservice) << extension << " {" << endl; |
| indent_up(); |
| for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) { |
| t_function* tfunc = (*func_iter); |
| string func_name = service_call_handler_function_name(tfunc); |
| string func_args = rust_sync_service_call_declaration(tfunc, false); |
| string func_return = to_rust_type(tfunc->get_returntype()); |
| render_rustdoc((t_doc*) tfunc); |
| f_gen_ |
| << indent() |
| << "fn " |
| << func_name << func_args |
| << " -> thrift::Result<" << func_return << ">;" |
| << endl; |
| } |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_sync_processor_definition_and_impl(t_service *tservice) { |
| string service_processor_name = rust_sync_processor_name(tservice); |
| string handler_trait_name = rust_sync_handler_trait_name(tservice); |
| |
| // struct |
| f_gen_ |
| << indent() |
| << "pub struct " << service_processor_name |
| << "<H: " << handler_trait_name |
| << "> {" |
| << endl; |
| indent_up(); |
| f_gen_ << indent() << "handler: H," << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| |
| // delegating impl |
| f_gen_ |
| << indent() |
| << "impl <H: " << handler_trait_name << "> " |
| << service_processor_name |
| << "<H> {" |
| << endl; |
| indent_up(); |
| f_gen_ << indent() << "pub fn new(handler: H) -> " << service_processor_name << "<H> {" << endl; |
| indent_up(); |
| f_gen_ << indent() << service_processor_name << " {" << endl; |
| indent_up(); |
| f_gen_ << indent() << "handler," << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| render_sync_process_delegation_functions(tservice); |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| |
| // actual impl |
| string service_actual_processor_name = rust_sync_processor_impl_name(tservice); |
| f_gen_ << indent() << "pub struct " << service_actual_processor_name << ";" << endl; |
| f_gen_ << endl; |
| f_gen_ << indent() << "impl " << service_actual_processor_name << " {" << endl; |
| indent_up(); |
| |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator func_iter; |
| for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) { |
| t_function* tfunc = (*func_iter); |
| render_sync_process_function(tfunc, handler_trait_name); |
| } |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| |
| // processor impl |
| f_gen_ |
| << indent() |
| << "impl <H: " |
| << handler_trait_name << "> TProcessor for " |
| << service_processor_name |
| << "<H> {" |
| << endl; |
| indent_up(); |
| |
| f_gen_ |
| << indent() |
| << "fn process(&self, i_prot: &mut dyn TInputProtocol, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" |
| << endl; |
| indent_up(); |
| |
| f_gen_ << indent() << "let message_ident = i_prot.read_message_begin()?;" << endl; |
| |
| f_gen_ << indent() << "let res = match &*message_ident.name {" << endl; // [sigh] explicit deref coercion |
| indent_up(); |
| render_process_match_statements(tservice); |
| f_gen_ << indent() << "method => {" << endl; |
| indent_up(); |
| render_thrift_error( |
| "Application", |
| "ApplicationError", |
| "ApplicationErrorKind::UnknownMethod", |
| "format!(\"unknown method {}\", method)" |
| ); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; |
| f_gen_ << indent() << "thrift::server::handle_process_result(&message_ident, res, o_prot)" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| f_gen_ << endl; |
| } |
| |
| void t_rs_generator::render_sync_process_delegation_functions(t_service *tservice) { |
| string actual_processor(rust_namespace(tservice) + rust_sync_processor_impl_name(tservice)); |
| |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator func_iter; |
| for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) { |
| t_function* tfunc = (*func_iter); |
| string function_name("process_" + rust_snake_case(tfunc->get_name())); |
| f_gen_ |
| << indent() |
| << "fn " << function_name |
| << "(&self, " |
| << "incoming_sequence_number: i32, " |
| << "i_prot: &mut dyn TInputProtocol, " |
| << "o_prot: &mut dyn TOutputProtocol) " |
| << "-> thrift::Result<()> {" |
| << endl; |
| indent_up(); |
| |
| f_gen_ |
| << indent() |
| << actual_processor |
| << "::" << function_name |
| << "(" |
| << "&self.handler, " |
| << "incoming_sequence_number, " |
| << "i_prot, " |
| << "o_prot" |
| << ")" |
| << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| t_service* extends = tservice->get_extends(); |
| if (extends) { |
| render_sync_process_delegation_functions(extends); |
| } |
| } |
| |
| void t_rs_generator::render_process_match_statements(t_service* tservice) { |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator func_iter; |
| for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) { |
| t_function* tfunc = (*func_iter); |
| f_gen_ << indent() << "\"" << tfunc->get_name() << "\"" << " => {" << endl; // note: use *original* name |
| indent_up(); |
| f_gen_ |
| << indent() |
| << "self.process_" << rust_snake_case(tfunc->get_name()) |
| << "(message_ident.sequence_number, i_prot, o_prot)" |
| << endl; |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| } |
| |
| t_service* extends = tservice->get_extends(); |
| if (extends) { |
| render_process_match_statements(extends); |
| } |
| } |
| |
| void t_rs_generator::render_sync_process_function(t_function *tfunc, const string &handler_type) { |
| string sequence_number_param("incoming_sequence_number"); |
| string output_protocol_param("o_prot"); |
| |
| if (tfunc->is_oneway()) { |
| sequence_number_param = "_"; |
| output_protocol_param = "_"; |
| } |
| |
| f_gen_ |
| << indent() |
| << "pub fn process_" << rust_snake_case(tfunc->get_name()) |
| << "<H: " << handler_type << ">" |
| << "(handler: &H, " |
| << sequence_number_param << ": i32, " |
| << "i_prot: &mut dyn TInputProtocol, " |
| << output_protocol_param << ": &mut dyn TOutputProtocol) " |
| << "-> thrift::Result<()> {" |
| << endl; |
| |
| indent_up(); |
| |
| // *always* read arguments from the input protocol |
| f_gen_ |
| << indent() |
| << "let " |
| << (has_non_void_args(tfunc) ? "args" : "_") |
| << " = " |
| << service_call_args_struct_name(tfunc) |
| << "::read_from_in_protocol(i_prot)?;" |
| << endl; |
| |
| f_gen_ |
| << indent() |
| << "match handler." |
| << service_call_handler_function_name(tfunc) |
| << rust_sync_service_call_invocation(tfunc, "args.") |
| << " {" |
| << endl; // start match |
| indent_up(); |
| |
| // handler succeeded |
| string handler_return_variable = tfunc->is_oneway() || tfunc->get_returntype()->is_void() ? "_" : "handler_return"; |
| f_gen_ << indent() << "Ok(" << handler_return_variable << ") => {" << endl; |
| indent_up(); |
| render_sync_handler_succeeded(tfunc); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| // handler failed |
| f_gen_ << indent() << "Err(e) => {" << endl; |
| indent_up(); |
| render_sync_handler_failed(tfunc); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; // end match |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; // end function |
| } |
| |
| void t_rs_generator::render_sync_handler_succeeded(t_function *tfunc) { |
| if (tfunc->is_oneway()) { |
| f_gen_ << indent() << "Ok(())" << endl; |
| } else { |
| f_gen_ |
| << indent() |
| << "let message_ident = TMessageIdentifier::new(" |
| << "\"" << tfunc->get_name() << "\", " // note: use *original* name |
| << "TMessageType::Reply, " |
| << "incoming_sequence_number);" |
| << endl; |
| f_gen_ << indent() << "o_prot.write_message_begin(&message_ident)?;" << endl; |
| f_gen_ << indent() << "let ret = " << handler_successful_return_struct(tfunc) <<";" << endl; |
| f_gen_ << indent() << "ret.write_to_out_protocol(o_prot)?;" << endl; |
| f_gen_ << indent() << "o_prot.write_message_end()?;" << endl; |
| f_gen_ << indent() << "o_prot.flush()" << endl; |
| } |
| } |
| |
| void t_rs_generator::render_sync_handler_failed(t_function *tfunc) { |
| string err_var("e"); |
| |
| f_gen_ << indent() << "match " << err_var << " {" << endl; |
| indent_up(); |
| |
| // if there are any user-defined exceptions for this service call handle them first |
| if (tfunc->get_xceptions() != nullptr && tfunc->get_xceptions()->get_sorted_members().size() > 0) { |
| string user_err_var("usr_err"); |
| f_gen_ << indent() << "thrift::Error::User(" << user_err_var << ") => {" << endl; |
| indent_up(); |
| render_sync_handler_failed_user_exception_branch(tfunc); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| } |
| |
| // application error |
| string app_err_var("app_err"); |
| f_gen_ << indent() << "thrift::Error::Application(" << app_err_var << ") => {" << endl; |
| indent_up(); |
| render_sync_handler_failed_application_exception_branch(tfunc, app_err_var); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| |
| // default case |
| f_gen_ << indent() << "_ => {" << endl; |
| indent_up(); |
| render_sync_handler_failed_default_exception_branch(tfunc); |
| indent_down(); |
| f_gen_ << indent() << "}," << endl; |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_sync_handler_failed_user_exception_branch(t_function *tfunc) { |
| if (tfunc->get_xceptions() == nullptr || tfunc->get_xceptions()->get_sorted_members().empty()) { |
| throw "cannot render user exception branches if no user exceptions defined"; |
| } |
| |
| const vector<t_field*> txceptions = tfunc->get_xceptions()->get_sorted_members(); |
| vector<t_field*>::const_iterator xception_iter; |
| int branches_rendered = 0; |
| |
| // run through all user-defined exceptions |
| for (xception_iter = txceptions.begin(); xception_iter != txceptions.end(); ++xception_iter) { |
| t_field* xception_field = (*xception_iter); |
| |
| string if_statement(branches_rendered == 0 ? "if usr_err" : "} else if usr_err"); |
| string exception_type(to_rust_type(xception_field->get_type())); |
| f_gen_ << indent() << if_statement << ".downcast_ref::<" << exception_type << ">().is_some() {" << endl; |
| indent_up(); |
| |
| f_gen_ |
| << indent() |
| << "let err = usr_err.downcast::<" << exception_type << ">().expect(\"downcast already checked\");" |
| << endl; |
| |
| // render the members of the return struct |
| ostringstream members; |
| |
| bool has_result_variable = !(tfunc->is_oneway() || tfunc->get_returntype()->is_void()); |
| if (has_result_variable) { |
| members << SERVICE_RESULT_VARIABLE << ": None, "; |
| } |
| |
| vector<t_field*>::const_iterator xception_members_iter; |
| for(xception_members_iter = txceptions.begin(); xception_members_iter != txceptions.end(); ++xception_members_iter) { |
| t_field* member = (*xception_members_iter); |
| string member_name(rust_field_name(member)); |
| if (member == xception_field) { |
| members << member_name << ": Some(*err), "; |
| } else { |
| members << member_name << ": None, "; |
| } |
| } |
| |
| string member_string = members.str(); |
| member_string.replace(member_string.size() - 2, 2, " "); // trim trailing comma |
| |
| // now write out the return struct |
| f_gen_ |
| << indent() |
| << "let ret_err = " |
| << service_call_result_struct_name(tfunc) |
| << "{ " << member_string << "};" |
| << endl; |
| |
| f_gen_ |
| << indent() |
| << "let message_ident = " |
| << "TMessageIdentifier::new(" |
| << "\"" << tfunc->get_name() << "\", " // note: use *original* name |
| << "TMessageType::Reply, " |
| << "incoming_sequence_number);" |
| << endl; |
| f_gen_ << indent() << "o_prot.write_message_begin(&message_ident)?;" << endl; |
| f_gen_ << indent() << "ret_err.write_to_out_protocol(o_prot)?;" << endl; |
| f_gen_ << indent() << "o_prot.write_message_end()?;" << endl; |
| f_gen_ << indent() << "o_prot.flush()" << endl; |
| |
| indent_down(); |
| |
| branches_rendered++; |
| } |
| |
| // the catch all, if somehow it was a user exception that we don't support |
| f_gen_ << indent() << "} else {" << endl; |
| indent_up(); |
| |
| // FIXME: same as default block below |
| |
| f_gen_ << indent() << "let ret_err = {" << endl; |
| indent_up(); |
| render_thrift_error_struct("ApplicationError", "ApplicationErrorKind::Unknown", "usr_err.description()"); |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; |
| render_sync_handler_send_exception_response(tfunc, "ret_err"); |
| |
| indent_down(); |
| f_gen_ << indent() << "}" << endl; |
| } |
| |
| void t_rs_generator::render_sync_handler_failed_application_exception_branch( |
| t_function *tfunc, |
| const string &app_err_var |
| ) { |
| if (tfunc->is_oneway()) { |
| f_gen_ << indent() << "Err(thrift::Error::Application(" << app_err_var << "))" << endl; |
| } else { |
| render_sync_handler_send_exception_response(tfunc, app_err_var); |
| } |
| } |
| |
| void t_rs_generator::render_sync_handler_failed_default_exception_branch(t_function *tfunc) { |
| f_gen_ << indent() << "let ret_err = {" << endl; |
| indent_up(); |
| render_thrift_error_struct("ApplicationError", "ApplicationErrorKind::Unknown", "e.description()"); |
| indent_down(); |
| f_gen_ << indent() << "};" << endl; |
| if (tfunc->is_oneway()) { |
| f_gen_ << indent() << "Err(thrift::Error::Application(ret_err))" << endl; |
| } else { |
| render_sync_handler_send_exception_response(tfunc, "ret_err"); |
| } |
| } |
| |
| void t_rs_generator::render_sync_handler_send_exception_response(t_function *tfunc, const string &err_var) { |
| f_gen_ |
| << indent() |
| << "let message_ident = TMessageIdentifier::new(" |
| << "\"" << tfunc->get_name() << "\", " // note: use *original* name |
| << "TMessageType::Exception, " |
| << "incoming_sequence_number);" |
| << endl; |
| f_gen_ << indent() << "o_prot.write_message_begin(&message_ident)?;" << endl; |
| f_gen_ << indent() << "thrift::Error::write_application_error_to_out_protocol(&" << err_var << ", o_prot)?;" << endl; |
| f_gen_ << indent() << "o_prot.write_message_end()?;" << endl; |
| f_gen_ << indent() << "o_prot.flush()" << endl; |
| } |
| |
| string t_rs_generator::handler_successful_return_struct(t_function* tfunc) { |
| int member_count = 0; |
| ostringstream return_struct; |
| |
| return_struct << service_call_result_struct_name(tfunc) << " { "; |
| |
| // actual return |
| if (!tfunc->get_returntype()->is_void()) { |
| return_struct << "result_value: Some(handler_return)"; |
| member_count++; |
| } |
| |
| // any user-defined exceptions |
| if (tfunc->get_xceptions() != nullptr) { |
| t_struct* txceptions = tfunc->get_xceptions(); |
| const vector<t_field*> members = txceptions->get_sorted_members(); |
| vector<t_field*>::const_iterator members_iter; |
| for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { |
| t_field* xception_field = (*members_iter); |
| if (member_count > 0) { return_struct << ", "; } |
| return_struct << rust_field_name(xception_field) << ": None"; |
| member_count++; |
| } |
| } |
| |
| return_struct << " }"; |
| |
| return return_struct.str(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // Utility |
| // |
| //----------------------------------------------------------------------------- |
| |
| void t_rs_generator::render_type_comment(const string& type_name) { |
| f_gen_ << "//" << endl; |
| f_gen_ << "// " << type_name << endl; |
| f_gen_ << "//" << endl; |
| f_gen_ << endl; |
| } |
| |
| // NOTE: do *not* put in an extra newline after doc is generated. |
| // This is because rust docs have to abut the line they're documenting. |
| void t_rs_generator::render_rustdoc(t_doc* tdoc) { |
| if (!tdoc->has_doc()) { |
| return; |
| } |
| |
| generate_docstring_comment(f_gen_, "", "/// ", tdoc->get_doc(), ""); |
| } |
| |
| void t_rs_generator::render_thrift_error( |
| const string& error_kind, |
| const string& error_struct, |
| const string& sub_error_kind, |
| const string& error_message |
| ) { |
| f_gen_ << indent() << "Err(" << endl; |
| indent_up(); |
| f_gen_ << indent() << "thrift::Error::" << error_kind << "(" << endl; |
| indent_up(); |
| render_thrift_error_struct(error_struct, sub_error_kind, error_message); |
| indent_down(); |
| f_gen_ << indent() << ")" << endl; |
| indent_down(); |
| f_gen_ << indent() << ")" << endl; |
| } |
| |
| void t_rs_generator::render_thrift_error_struct( |
| const string& error_struct, |
| const string& sub_error_kind, |
| const string& error_message |
| ) { |
| f_gen_ << indent() << error_struct << "::new(" << endl; |
| indent_up(); |
| f_gen_ << indent() << sub_error_kind << "," << endl; |
| f_gen_ << indent() << error_message << endl; |
| indent_down(); |
| f_gen_ << indent() << ")" << endl; |
| } |
| |
| bool t_rs_generator::is_double(t_type* ttype) { |
| ttype = get_true_type(ttype); |
| if (ttype->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); |
| if (tbase == t_base_type::TYPE_DOUBLE) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| string t_rs_generator::to_rust_type(t_type* ttype, bool ordered_float) { |
| // ttype = get_true_type(ttype); <-- recurses through as many typedef layers as necessary |
| if (ttype->is_base_type()) { |
| t_base_type* tbase_type = ((t_base_type*)ttype); |
| switch (tbase_type->get_base()) { |
| case t_base_type::TYPE_VOID: |
| return "()"; |
| case t_base_type::TYPE_STRING: |
| if (tbase_type->is_binary()) { |
| return "Vec<u8>"; |
| } else { |
| return "String"; |
| } |
| case t_base_type::TYPE_BOOL: |
| return "bool"; |
| case t_base_type::TYPE_I8: |
| return "i8"; |
| case t_base_type::TYPE_I16: |
| return "i16"; |
| case t_base_type::TYPE_I32: |
| return "i32"; |
| case t_base_type::TYPE_I64: |
| return "i64"; |
| case t_base_type::TYPE_DOUBLE: |
| if (ordered_float) { |
| return "OrderedFloat<f64>"; |
| } else { |
| return "f64"; |
| } |
| } |
| } else if (ttype->is_typedef()) { |
| t_typedef* ttypedef = (t_typedef*)ttype; |
| string rust_type = rust_namespace(ttype) + ttypedef->get_symbolic(); |
| rust_type = ttypedef->is_forward_typedef() ? "Box<" + rust_type + ">" : rust_type; |
| return rust_type; |
| } else if (ttype->is_enum()) { |
| return rust_namespace(ttype) + rust_camel_case(ttype->get_name()); |
| } else if (ttype->is_struct() || ttype->is_xception()) { |
| return rust_namespace(ttype) + rust_camel_case(ttype->get_name()); |
| } else if (ttype->is_map()) { |
| t_map* tmap = (t_map*)ttype; |
| return "BTreeMap<" + to_rust_type(tmap->get_key_type()) + ", " + to_rust_type(tmap->get_val_type()) + ">"; |
| } else if (ttype->is_set()) { |
| t_set* tset = (t_set*)ttype; |
| return "BTreeSet<" + to_rust_type(tset->get_elem_type()) + ">"; |
| } else if (ttype->is_list()) { |
| t_list* tlist = (t_list*)ttype; |
| return "Vec<" + to_rust_type(tlist->get_elem_type()) + ">"; |
| } |
| |
| throw "cannot find rust type for " + ttype->get_name(); |
| } |
| |
| string t_rs_generator::to_rust_const_type(t_type* ttype, bool ordered_float) { |
| if (ttype->is_base_type()) { |
| t_base_type* tbase_type = ((t_base_type*)ttype); |
| if (tbase_type->get_base() == t_base_type::TYPE_STRING) { |
| if (tbase_type->is_binary()) { |
| return "&[u8]"; |
| } else { |
| return "&str"; |
| } |
| } |
| } |
| |
| return to_rust_type(ttype, ordered_float); |
| } |
| |
| string t_rs_generator::to_rust_field_type_enum(t_type* ttype) { |
| ttype = get_true_type(ttype); |
| if (ttype->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); |
| switch (tbase) { |
| case t_base_type::TYPE_VOID: |
| throw "will not generate protocol::TType for TYPE_VOID"; |
| case t_base_type::TYPE_STRING: // both strings and binary are actually encoded as TType::String |
| return "TType::String"; |
| case t_base_type::TYPE_BOOL: |
| return "TType::Bool"; |
| case t_base_type::TYPE_I8: |
| return "TType::I08"; |
| case t_base_type::TYPE_I16: |
| return "TType::I16"; |
| case t_base_type::TYPE_I32: |
| return "TType::I32"; |
| case t_base_type::TYPE_I64: |
| return "TType::I64"; |
| case t_base_type::TYPE_DOUBLE: |
| return "TType::Double"; |
| } |
| } else if (ttype->is_enum()) { |
| return "TType::I32"; |
| } else if (ttype->is_struct() || ttype->is_xception()) { |
| return "TType::Struct"; |
| } else if (ttype->is_map()) { |
| return "TType::Map"; |
| } else if (ttype->is_set()) { |
| return "TType::Set"; |
| } else if (ttype->is_list()) { |
| return "TType::List"; |
| } |
| |
| throw "cannot find TType for " + ttype->get_name(); |
| } |
| |
| string t_rs_generator::opt_in_req_out_value(t_type* ttype) { |
| ttype = get_true_type(ttype); |
| if (ttype->is_base_type()) { |
| t_base_type* tbase_type = ((t_base_type*)ttype); |
| switch (tbase_type->get_base()) { |
| case t_base_type::TYPE_VOID: |
| throw "cannot generate OPT_IN_REQ_OUT value for void"; |
| case t_base_type::TYPE_STRING: |
| if (tbase_type->is_binary()) { |
| return "Some(Vec::new())"; |
| } else { |
| return "Some(\"\".to_owned())"; |
| } |
| case t_base_type::TYPE_BOOL: |
| return "Some(false)"; |
| case t_base_type::TYPE_I8: |
| case t_base_type::TYPE_I16: |
| case t_base_type::TYPE_I32: |
| case t_base_type::TYPE_I64: |
| return "Some(0)"; |
| case t_base_type::TYPE_DOUBLE: |
| return "Some(OrderedFloat::from(0.0))"; |
| } |
| |
| } else if (ttype->is_enum() || ttype->is_struct() || ttype->is_xception()) { |
| return "None"; |
| } else if (ttype->is_list()) { |
| return "Some(Vec::new())"; |
| } else if (ttype->is_set()) { |
| return "Some(BTreeSet::new())"; |
| } else if (ttype->is_map()) { |
| return "Some(BTreeMap::new())"; |
| } |
| |
| throw "cannot generate opt-in-req-out value for type " + ttype->get_name(); |
| } |
| |
| bool t_rs_generator::can_generate_simple_const(t_type* ttype) { |
| t_type* actual_type = get_true_type(ttype); |
| if (actual_type->is_base_type()) { |
| t_base_type* tbase_type = (t_base_type*)actual_type; |
| return !(tbase_type->get_base() == t_base_type::TYPE_DOUBLE); |
| } else { |
| return false; |
| } |
| } |
| |
| bool t_rs_generator::can_generate_const_holder(t_type* ttype) { |
| t_type* actual_type = get_true_type(ttype); |
| return !can_generate_simple_const(actual_type) && !actual_type->is_service(); |
| } |
| |
| bool t_rs_generator::is_void(t_type* ttype) { |
| return ttype->is_base_type() && ((t_base_type*)ttype)->get_base() == t_base_type::TYPE_VOID; |
| } |
| |
| bool t_rs_generator::is_optional(t_field::e_req req) { |
| return req == t_field::T_OPTIONAL || req == t_field::T_OPT_IN_REQ_OUT; |
| } |
| |
| t_field::e_req t_rs_generator::actual_field_req(t_field* tfield, t_rs_generator::e_struct_type struct_type) { |
| return struct_type == t_rs_generator::T_ARGS ? t_field::T_REQUIRED : tfield->get_req(); |
| } |
| |
| bool t_rs_generator::has_args(t_function* tfunc) { |
| return tfunc->get_arglist() != nullptr && !tfunc->get_arglist()->get_sorted_members().empty(); |
| } |
| |
| bool t_rs_generator::has_non_void_args(t_function* tfunc) { |
| bool has_non_void_args = false; |
| |
| const vector<t_field*> args = tfunc->get_arglist()->get_sorted_members(); |
| vector<t_field*>::const_iterator args_iter; |
| for (args_iter = args.begin(); args_iter != args.end(); ++args_iter) { |
| t_field* tfield = (*args_iter); |
| if (!tfield->get_type()->is_void()) { |
| has_non_void_args = true; |
| break; |
| } |
| } |
| |
| return has_non_void_args; |
| } |
| |
| string t_rs_generator::visibility_qualifier(t_rs_generator::e_struct_type struct_type) { |
| switch(struct_type) { |
| case t_rs_generator::T_ARGS: |
| case t_rs_generator::T_RESULT: |
| return ""; |
| default: |
| return "pub "; |
| } |
| } |
| |
| string t_rs_generator::rust_namespace(t_service* tservice) { |
| if (tservice->get_program()->get_name() != get_program()->get_name()) { |
| return rust_snake_case(tservice->get_program()->get_name()) + "::"; |
| } else { |
| return ""; |
| } |
| } |
| |
| string t_rs_generator::rust_namespace(t_type* ttype) { |
| if (ttype->get_program()->get_name() != get_program()->get_name()) { |
| return rust_snake_case(ttype->get_program()->get_name()) + "::"; |
| } else { |
| return ""; |
| } |
| } |
| |
| bool t_rs_generator::is_reserved(const string& name) { |
| return RUST_RESERVED_WORDS_SET.find(name) != RUST_RESERVED_WORDS_SET.end(); |
| } |
| |
| string t_rs_generator::rust_struct_name(t_struct* tstruct) { |
| string base_struct_name(rust_camel_case(tstruct->get_name())); |
| return rust_safe_name(base_struct_name); |
| } |
| |
| string t_rs_generator::rust_field_name(t_field* tfield) { |
| string base_field_name(rust_snake_case(tfield->get_name())); |
| return rust_safe_name(base_field_name); |
| } |
| |
| string t_rs_generator::rust_union_field_name(t_field* tfield) { |
| string base_field_name(rust_camel_case(tfield->get_name())); |
| return rust_safe_name(base_field_name); |
| } |
| |
| string t_rs_generator::rust_safe_name(const string& name) { |
| if (is_reserved(name)) { |
| return name + "_"; |
| } else { |
| return name; |
| } |
| } |
| |
| string t_rs_generator::service_call_client_function_name(t_function* tfunc) { |
| return rust_snake_case(tfunc->get_name()); |
| } |
| |
| string t_rs_generator::service_call_handler_function_name(t_function* tfunc) { |
| return "handle_" + rust_snake_case(tfunc->get_name()); |
| } |
| |
| string t_rs_generator::service_call_args_struct_name(t_function* tfunc) { |
| // Thrift automatically appends `Args` to the arglist name. No need to do it here. |
| return rust_camel_case(service_name_) + rust_camel_case(tfunc->get_arglist()->get_name()); |
| } |
| |
| string t_rs_generator::service_call_result_struct_name(t_function* tfunc) { |
| return rust_camel_case(service_name_) + rust_camel_case(tfunc->get_name()) + RESULT_STRUCT_SUFFIX; |
| } |
| |
| string t_rs_generator::rust_sync_client_marker_trait_name(t_service* tservice) { |
| return "T" + rust_camel_case(tservice->get_name()) + "SyncClientMarker"; |
| } |
| |
| string t_rs_generator::rust_sync_client_trait_name(t_service* tservice) { |
| return "T" + rust_camel_case(tservice->get_name()) + "SyncClient"; |
| } |
| |
| string t_rs_generator::rust_sync_client_impl_name(t_service* tservice) { |
| return rust_camel_case(tservice->get_name()) + "SyncClient"; |
| } |
| |
| string t_rs_generator::rust_sync_handler_trait_name(t_service* tservice) { |
| return rust_camel_case(tservice->get_name()) + "SyncHandler"; |
| } |
| |
| string t_rs_generator::rust_sync_processor_name(t_service* tservice) { |
| return rust_camel_case(tservice->get_name()) + "SyncProcessor"; |
| } |
| |
| string t_rs_generator::rust_sync_processor_impl_name(t_service *tservice) { |
| return "T" + rust_camel_case(tservice->get_name()) + "ProcessFunctions"; |
| } |
| |
| string t_rs_generator::rust_enum_variant_name(const string &name) { |
| bool all_uppercase = true; |
| |
| for (char i : name) { |
| if (isalnum(i) && islower(i)) { |
| all_uppercase = false; |
| break; |
| } |
| } |
| |
| if (all_uppercase) { |
| return capitalize(camelcase(lowercase(name))); |
| } else { |
| return capitalize(camelcase(name)); |
| } |
| } |
| |
| string t_rs_generator::rust_upper_case(const string& name) { |
| string str(uppercase(underscore(name))); |
| string_replace(str, "__", "_"); |
| return str; |
| } |
| |
| string t_rs_generator::rust_snake_case(const string& name) { |
| string str(decapitalize(underscore(name))); |
| string_replace(str, "__", "_"); |
| return str; |
| } |
| |
| string t_rs_generator::rust_camel_case(const string& name) { |
| string str(capitalize(camelcase(name))); |
| string_replace(str, "_", ""); |
| return str; |
| } |
| |
| string t_rs_generator::rust_safe_field_id(int32_t id) { |
| string id_str = std::to_string(abs(id)); |
| if (id >= 0) { |
| return id_str; |
| } else { |
| string str("neg"); |
| str += id_str; |
| return str; |
| } |
| } |
| |
| void t_rs_generator::string_replace(string& target, const string& search_string, const string& replace_string) { |
| if (target.empty()) { |
| return; |
| } |
| |
| size_t match_len = search_string.length(); |
| size_t replace_len = replace_string.length(); |
| |
| size_t search_idx = 0; |
| size_t match_idx; |
| while ((match_idx = target.find(search_string, search_idx)) != string::npos) { |
| target.replace(match_idx, match_len, replace_string); |
| search_idx = match_idx + replace_len; |
| } |
| } |
| |
| THRIFT_REGISTER_GENERATOR( |
| rs, |
| "Rust", |
| "\n") // no Rust-generator-specific options |