diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | compiler/cpp/src/thrift/generate/t_rs_generator.cc | 40 | ||||
-rw-r--r-- | lib/rs/test/Makefile.am | 3 | ||||
-rw-r--r-- | lib/rs/test/src/bin/kitchen_sink_client.rs | 160 | ||||
-rw-r--r-- | lib/rs/test/src/bin/kitchen_sink_server.rs | 56 | ||||
-rw-r--r-- | lib/rs/test/src/lib.rs | 1 |
6 files changed, 240 insertions, 21 deletions
diff --git a/.gitignore b/.gitignore index 88bb9c6fc..00cf8bb33 100644 --- a/.gitignore +++ b/.gitignore @@ -273,6 +273,7 @@ project.lock.json /lib/rs/test/src/base_one.rs /lib/rs/test/src/base_two.rs /lib/rs/test/src/midlayer.rs +/lib/rs/test/src/recursive.rs /lib/rs/test/src/ultimate.rs /lib/rs/*.iml /lib/rs/**/*.iml diff --git a/compiler/cpp/src/thrift/generate/t_rs_generator.cc b/compiler/cpp/src/thrift/generate/t_rs_generator.cc index 1f1e1d879..89afd7a1d 100644 --- a/compiler/cpp/src/thrift/generate/t_rs_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_rs_generator.cc @@ -260,7 +260,8 @@ private: 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. - void render_type_sync_read(const string &type_var, t_type *ttype); + // 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`. @@ -353,12 +354,28 @@ private: string handler_successful_return_struct(t_function* tfunc); + // Writes the result of `render_rift_error_struct` wrapped in an `Err(thrift::Error(...))`. void render_rift_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_rift_error_struct( const string& error_struct, const string& sub_error_kind, @@ -1858,7 +1875,7 @@ void t_rs_generator::render_union_sync_read(const string &union_name, t_struct * } // 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) { +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()) { @@ -1891,13 +1908,23 @@ void t_rs_generator::render_type_sync_read(const string &type_var, t_type *ttype 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()); + 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 << " = " << to_rust_type(ttype) << "::read_from_in_protocol(i_prot)?;" + << "let " << type_var << " = " << read_call << ";" << endl; return; } else if (ttype->is_map()) { @@ -2979,7 +3006,10 @@ string t_rs_generator::to_rust_type(t_type* ttype, bool ordered_float) { } } } else if (ttype->is_typedef()) { - return rust_namespace(ttype) + ((t_typedef*)ttype)->get_symbolic(); + 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) + ttype->get_name(); } else if (ttype->is_struct() || ttype->is_xception()) { diff --git a/lib/rs/test/Makefile.am b/lib/rs/test/Makefile.am index 88969404b..87208d7ba 100644 --- a/lib/rs/test/Makefile.am +++ b/lib/rs/test/Makefile.am @@ -19,11 +19,12 @@ THRIFT = $(top_builddir)/compiler/cpp/thrift -stubs: thrifts/Base_One.thrift thrifts/Base_Two.thrift thrifts/Midlayer.thrift thrifts/Ultimate.thrift $(THRIFT) +stubs: thrifts/Base_One.thrift thrifts/Base_Two.thrift thrifts/Midlayer.thrift thrifts/Ultimate.thrift $(top_builddir)/test/Recursive.thrift $(THRIFT) $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Base_One.thrift $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Base_Two.thrift $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Midlayer.thrift $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Ultimate.thrift + $(THRIFT) -out src --gen rs $(top_builddir)/test/Recursive.thrift check: stubs $(CARGO) build diff --git a/lib/rs/test/src/bin/kitchen_sink_client.rs b/lib/rs/test/src/bin/kitchen_sink_client.rs index 9738298cb..fb6ea15cc 100644 --- a/lib/rs/test/src/bin/kitchen_sink_client.rs +++ b/lib/rs/test/src/bin/kitchen_sink_client.rs @@ -21,8 +21,12 @@ extern crate clap; extern crate kitchen_sink; extern crate thrift; +use std::convert::Into; + use kitchen_sink::base_two::{TNapkinServiceSyncClient, TRamenServiceSyncClient}; use kitchen_sink::midlayer::{MealServiceSyncClient, TMealServiceSyncClient}; +use kitchen_sink::recursive; +use kitchen_sink::recursive::{CoRec, CoRec2, RecList, RecTree, TTestServiceSyncClient}; use kitchen_sink::ultimate::{FullMealServiceSyncClient, TFullMealServiceSyncClient}; use thrift::transport::{ReadHalf, TFramedReadTransport, TFramedWriteTransport, TIoChannel, TTcpChannel, WriteHalf}; @@ -47,7 +51,7 @@ fn run() -> thrift::Result<()> { (@arg host: --host +takes_value "Host on which the Thrift test server is located") (@arg port: --port +takes_value "Port on which the Thrift test server is listening") (@arg protocol: --protocol +takes_value "Thrift protocol implementation to use (\"binary\", \"compact\")") - (@arg service: --service +takes_value "Service type to contact (\"part\", \"full\")") + (@arg service: --service +takes_value "Service type to contact (\"part\", \"full\", \"recursive\")") ) .get_matches(); @@ -80,8 +84,9 @@ fn run_client( o_prot: Box<TOutputProtocol>, ) -> thrift::Result<()> { match service { - "full" => run_full_meal_service(i_prot, o_prot), - "part" => run_meal_service(i_prot, o_prot), + "full" => exec_full_meal_client(i_prot, o_prot), + "part" => exec_meal_client(i_prot, o_prot), + "recursive" => exec_recursive_client(i_prot, o_prot), _ => Err(thrift::Error::from(format!("unknown service type {}", service)),), } } @@ -95,7 +100,7 @@ fn tcp_channel( c.split() } -fn run_meal_service( +fn exec_meal_client( i_prot: Box<TInputProtocol>, o_prot: Box<TOutputProtocol>, ) -> thrift::Result<()> { @@ -105,28 +110,155 @@ fn run_meal_service( // this is because the MealService struct does not contain the appropriate service marker // only the following three calls work - execute_call("part", "ramen", || client.ramen(50))?; - execute_call("part", "meal", || client.meal())?; - execute_call("part", "napkin", || client.napkin())?; + execute_call("part", "ramen", || client.ramen(50)) + .map(|_| ())?; + execute_call("part", "meal", || client.meal()) + .map(|_| ())?; + execute_call("part", "napkin", || client.napkin()) + .map(|_| ())?; Ok(()) } -fn run_full_meal_service( +fn exec_full_meal_client( i_prot: Box<TInputProtocol>, o_prot: Box<TOutputProtocol>, ) -> thrift::Result<()> { let mut client = FullMealServiceSyncClient::new(i_prot, o_prot); - execute_call("full", "ramen", || client.ramen(100))?; - execute_call("full", "meal", || client.meal())?; - execute_call("full", "napkin", || client.napkin())?; - execute_call("full", "full meal", || client.full_meal())?; + execute_call("full", "ramen", || client.ramen(100)) + .map(|_| ())?; + execute_call("full", "meal", || client.meal()) + .map(|_| ())?; + execute_call("full", "napkin", || client.napkin()) + .map(|_| ())?; + execute_call("full", "full meal", || client.full_meal()) + .map(|_| ())?; + + Ok(()) +} + +fn exec_recursive_client( + i_prot: Box<TInputProtocol>, + o_prot: Box<TOutputProtocol>, +) -> thrift::Result<()> { + let mut client = recursive::TestServiceSyncClient::new(i_prot, o_prot); + + let tree = RecTree { + children: Some( + vec![ + Box::new( + RecTree { + children: Some( + vec![ + Box::new( + RecTree { + children: None, + item: Some(3), + }, + ), + Box::new( + RecTree { + children: None, + item: Some(4), + }, + ), + ], + ), + item: Some(2), + }, + ), + ], + ), + item: Some(1), + }; + + let expected_tree = RecTree { + children: Some( + vec![ + Box::new( + RecTree { + children: Some( + vec![ + Box::new( + RecTree { + children: Some(Vec::new()), // remote returns an empty list + item: Some(3), + }, + ), + Box::new( + RecTree { + children: Some(Vec::new()), // remote returns an empty list + item: Some(4), + }, + ), + ], + ), + item: Some(2), + }, + ), + ], + ), + item: Some(1), + }; + + let returned_tree = execute_call("recursive", "echo_tree", || client.echo_tree(tree.clone()))?; + if returned_tree != expected_tree { + return Err( + format!( + "mismatched recursive tree {:?} {:?}", + expected_tree, + returned_tree + ) + .into(), + ); + } + + let list = RecList { + nextitem: Some( + Box::new( + RecList { + nextitem: Some( + Box::new( + RecList { + nextitem: None, + item: Some(3), + }, + ), + ), + item: Some(2), + }, + ), + ), + item: Some(1), + }; + let returned_list = execute_call("recursive", "echo_list", || client.echo_list(list.clone()))?; + if returned_list != list { + return Err(format!("mismatched recursive list {:?} {:?}", list, returned_list).into(),); + } + + let co_rec = CoRec { + other: Some( + Box::new( + CoRec2 { + other: Some(CoRec { other: Some(Box::new(CoRec2 { other: None })) }), + }, + ), + ), + }; + let returned_co_rec = execute_call( + "recursive", + "echo_co_rec", + || client.echo_co_rec(co_rec.clone()), + )?; + if returned_co_rec != co_rec { + return Err(format!("mismatched co_rec {:?} {:?}", co_rec, returned_co_rec).into(),); + } Ok(()) } -fn execute_call<F, R>(service_type: &str, call_name: &str, mut f: F) -> thrift::Result<()> +fn execute_call<F, R>(service_type: &str, call_name: &str, mut f: F) -> thrift::Result<R> where F: FnMut() -> thrift::Result<R>, { @@ -144,5 +276,5 @@ where } } - res.map(|_| ()) + res } diff --git a/lib/rs/test/src/bin/kitchen_sink_server.rs b/lib/rs/test/src/bin/kitchen_sink_server.rs index 19112cdbb..15ceb29dc 100644 --- a/lib/rs/test/src/bin/kitchen_sink_server.rs +++ b/lib/rs/test/src/bin/kitchen_sink_server.rs @@ -24,6 +24,7 @@ extern crate thrift; use kitchen_sink::base_one::Noodle; use kitchen_sink::base_two::{Napkin, NapkinServiceSyncHandler, Ramen, RamenServiceSyncHandler}; use kitchen_sink::midlayer::{Dessert, Meal, MealServiceSyncHandler, MealServiceSyncProcessor}; +use kitchen_sink::recursive; use kitchen_sink::ultimate::{Drink, FullMeal, FullMealAndDrinks, FullMealAndDrinksServiceSyncProcessor, FullMealServiceSyncHandler}; use kitchen_sink::ultimate::FullMealAndDrinksServiceSyncHandler; @@ -52,7 +53,7 @@ fn run() -> thrift::Result<()> { (about: "Thrift Rust kitchen sink test server") (@arg port: --port +takes_value "port on which the test server listens") (@arg protocol: --protocol +takes_value "Thrift protocol implementation to use (\"binary\", \"compact\")") - (@arg service: --service +takes_value "Service type to contact (\"part\", \"full\")") + (@arg service: --service +takes_value "Service type to contact (\"part\", \"full\", \"recursive\")") ) .get_matches(); @@ -111,6 +112,15 @@ fn run() -> thrift::Result<()> { o_protocol_factory, ) } + "recursive" => { + run_recursive_server( + &listen_address, + r_transport_factory, + i_protocol_factory, + w_transport_factory, + o_protocol_factory, + ) + } unknown => Err(format!("unsupported service type {}", unknown).into()), } } @@ -248,3 +258,47 @@ fn ramen() -> Ramen { fn napkin() -> Napkin { Napkin {} } + +fn run_recursive_server<RTF, IPF, WTF, OPF>( + listen_address: &str, + r_transport_factory: RTF, + i_protocol_factory: IPF, + w_transport_factory: WTF, + o_protocol_factory: OPF, +) -> thrift::Result<()> +where + RTF: TReadTransportFactory + 'static, + IPF: TInputProtocolFactory + 'static, + WTF: TWriteTransportFactory + 'static, + OPF: TOutputProtocolFactory + 'static, +{ + let processor = recursive::TestServiceSyncProcessor::new(RecursiveTestServerHandler {}); + let mut server = TServer::new( + r_transport_factory, + i_protocol_factory, + w_transport_factory, + o_protocol_factory, + processor, + 1, + ); + + server.listen(listen_address) +} + +struct RecursiveTestServerHandler; +impl recursive::TestServiceSyncHandler for RecursiveTestServerHandler { + fn handle_echo_tree(&self, tree: recursive::RecTree) -> thrift::Result<recursive::RecTree> { + println!("{:?}", tree); + Ok(tree) + } + + fn handle_echo_list(&self, lst: recursive::RecList) -> thrift::Result<recursive::RecList> { + println!("{:?}", lst); + Ok(lst) + } + + fn handle_echo_co_rec(&self, item: recursive::CoRec) -> thrift::Result<recursive::CoRec> { + println!("{:?}", item); + Ok(item) + } +} diff --git a/lib/rs/test/src/lib.rs b/lib/rs/test/src/lib.rs index 53f487340..e5e176e14 100644 --- a/lib/rs/test/src/lib.rs +++ b/lib/rs/test/src/lib.rs @@ -23,6 +23,7 @@ pub mod base_one; pub mod base_two; pub mod midlayer; pub mod ultimate; +pub mod recursive; #[cfg(test)] mod tests { |