| // 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. |
| |
| use clap::{clap_app, value_t}; |
| |
| 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; |
| use thrift::protocol::{ |
| TBinaryInputProtocol, TBinaryOutputProtocol, TCompactInputProtocol, TCompactOutputProtocol, |
| TInputProtocol, TOutputProtocol, |
| }; |
| use thrift::transport::{ |
| ReadHalf, TFramedReadTransport, TFramedWriteTransport, TIoChannel, TTcpChannel, WriteHalf, |
| }; |
| |
| fn main() { |
| match run() { |
| Ok(()) => println!("kitchen sink client completed successfully"), |
| Err(e) => { |
| println!("kitchen sink client failed with error {:?}", e); |
| std::process::exit(1); |
| } |
| } |
| } |
| |
| fn run() -> thrift::Result<()> { |
| let matches = clap_app!(rust_kitchen_sink_client => |
| (version: "0.1.0") |
| (author: "Apache Thrift Developers <dev@thrift.apache.org>") |
| (about: "Thrift Rust kitchen sink client") |
| (@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\", \"recursive\")") |
| ) |
| .get_matches(); |
| |
| let host = matches.value_of("host").unwrap_or("127.0.0.1"); |
| let port = value_t!(matches, "port", u16).unwrap_or(9090); |
| let protocol = matches.value_of("protocol").unwrap_or("compact"); |
| let service = matches.value_of("service").unwrap_or("part"); |
| |
| let (i_chan, o_chan) = tcp_channel(host, port)?; |
| let (i_tran, o_tran) = ( |
| TFramedReadTransport::new(i_chan), |
| TFramedWriteTransport::new(o_chan), |
| ); |
| |
| let (i_prot, o_prot): (Box<dyn TInputProtocol>, Box<dyn TOutputProtocol>) = match protocol { |
| "binary" => ( |
| Box::new(TBinaryInputProtocol::new(i_tran, true)), |
| Box::new(TBinaryOutputProtocol::new(o_tran, true)), |
| ), |
| "compact" => ( |
| Box::new(TCompactInputProtocol::new(i_tran)), |
| Box::new(TCompactOutputProtocol::new(o_tran)), |
| ), |
| unmatched => return Err(format!("unsupported protocol {}", unmatched).into()), |
| }; |
| |
| run_client(service, i_prot, o_prot) |
| } |
| |
| fn run_client( |
| service: &str, |
| i_prot: Box<dyn TInputProtocol>, |
| o_prot: Box<dyn TOutputProtocol>, |
| ) -> thrift::Result<()> { |
| match service { |
| "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 |
| ))), |
| } |
| } |
| |
| fn tcp_channel( |
| host: &str, |
| port: u16, |
| ) -> thrift::Result<(ReadHalf<TTcpChannel>, WriteHalf<TTcpChannel>)> { |
| let mut c = TTcpChannel::new(); |
| c.open(&format!("{}:{}", host, port))?; |
| c.split() |
| } |
| |
| fn exec_meal_client( |
| i_prot: Box<dyn TInputProtocol>, |
| o_prot: Box<dyn TOutputProtocol>, |
| ) -> thrift::Result<()> { |
| let mut client = MealServiceSyncClient::new(i_prot, o_prot); |
| |
| // client.full_meal(); // <-- IMPORTANT: if you uncomment this, compilation *should* fail |
| // 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)).map(|_| ())?; |
| execute_call("part", "meal", || client.meal()).map(|_| ())?; |
| execute_call("part", "napkin", || client.napkin()).map(|_| ())?; |
| |
| Ok(()) |
| } |
| |
| fn exec_full_meal_client( |
| i_prot: Box<dyn TInputProtocol>, |
| o_prot: Box<dyn TOutputProtocol>, |
| ) -> thrift::Result<()> { |
| let mut client = FullMealServiceSyncClient::new(i_prot, o_prot); |
| |
| 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<dyn TInputProtocol>, |
| o_prot: Box<dyn 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>, |
| { |
| let res = f(); |
| |
| match res { |
| Ok(_) => println!("{}: completed {} call", service_type, call_name), |
| Err(ref e) => println!( |
| "{}: failed {} call with error {:?}", |
| service_type, call_name, e |
| ), |
| } |
| |
| res |
| } |