THRIFT-4196 Support recursive types in Rust
Client: rs
Patch: Allen George <allen.george@gmail.com>
This closes #1267
diff --git a/lib/rs/test/Makefile.am b/lib/rs/test/Makefile.am
index 8896940..87208d7 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 9738298..fb6ea15 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 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 @@
(@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 @@
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 @@
c.split()
}
-fn run_meal_service(
+fn exec_meal_client(
i_prot: Box<TInputProtocol>,
o_prot: Box<TOutputProtocol>,
) -> thrift::Result<()> {
@@ -105,28 +110,155 @@
// 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 execute_call<F, R>(service_type: &str, call_name: &str, mut f: F) -> thrift::Result<()>
+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<R>
where
F: FnMut() -> thrift::Result<R>,
{
@@ -144,5 +276,5 @@
}
}
- 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 19112cd..15ceb29 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 @@
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 @@
(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 @@
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 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 53f4873..e5e176e 100644
--- a/lib/rs/test/src/lib.rs
+++ b/lib/rs/test/src/lib.rs
@@ -23,6 +23,7 @@
pub mod base_two;
pub mod midlayer;
pub mod ultimate;
+pub mod recursive;
#[cfg(test)]
mod tests {