THRIFT-2945 Add Rust support
Client: Rust
Patch: Allen George <allen.george@gmail.com>
This closes #1147
diff --git a/tutorial/rs/src/bin/tutorial_client.rs b/tutorial/rs/src/bin/tutorial_client.rs
new file mode 100644
index 0000000..2b0d4f9
--- /dev/null
+++ b/tutorial/rs/src/bin/tutorial_client.rs
@@ -0,0 +1,136 @@
+// 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.
+
+#[macro_use]
+extern crate clap;
+
+extern crate thrift;
+extern crate thrift_tutorial;
+
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use thrift::protocol::{TInputProtocol, TOutputProtocol};
+use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol};
+use thrift::transport::{TFramedTransport, TTcpTransport, TTransport};
+
+use thrift_tutorial::shared::TSharedServiceSyncClient;
+use thrift_tutorial::tutorial::{CalculatorSyncClient, TCalculatorSyncClient, Operation, Work};
+
+fn main() {
+ match run() {
+ Ok(()) => println!("tutorial client ran successfully"),
+ Err(e) => {
+ println!("tutorial client failed with error {:?}", e);
+ std::process::exit(1);
+ }
+ }
+}
+
+fn run() -> thrift::Result<()> {
+ let options = clap_app!(rust_tutorial_client =>
+ (version: "0.1.0")
+ (author: "Apache Thrift Developers <dev@thrift.apache.org>")
+ (about: "Thrift Rust tutorial client")
+ (@arg host: --host +takes_value "host on which the tutorial server listens")
+ (@arg port: --port +takes_value "port on which the tutorial server listens")
+ );
+ let matches = options.get_matches();
+
+ // get any passed-in args or the defaults
+ let host = matches.value_of("host").unwrap_or("127.0.0.1");
+ let port = value_t!(matches, "port", u16).unwrap_or(9090);
+
+ // build our client and connect to the host:port
+ let mut client = new_client(host, port)?;
+
+ // alright!
+ // let's start making some calls
+
+ // let's start with a ping; the server should respond
+ println!("ping!");
+ client.ping()?;
+
+ // simple add
+ println!("add");
+ let res = client.add(1, 2)?;
+ println!("added 1, 2 and got {}", res);
+
+ let logid = 32;
+
+ // let's do...a multiply!
+ let res = client.calculate(logid, Work::new(7, 8, Operation::MULTIPLY, None))?;
+ println!("multiplied 7 and 8 and got {}", res);
+
+ // let's get the log for it
+ let res = client.get_struct(32)?;
+ println!("got log {:?} for operation {}", res, logid);
+
+ // ok - let's be bad :(
+ // do a divide by 0
+ // logid doesn't matter; won't be recorded
+ let res = client.calculate(77, Work::new(2, 0, Operation::DIVIDE, "we bad".to_owned()));
+
+ // we should have gotten an exception back
+ match res {
+ Ok(v) => panic!("should not have succeeded with result {}", v),
+ Err(e) => println!("divide by zero failed with error {:?}", e),
+ }
+
+ // let's do a one-way call
+ println!("zip");
+ client.zip()?;
+
+ // and then close out with a final ping
+ println!("ping!");
+ client.ping()?;
+
+ Ok(())
+}
+
+fn new_client(host: &str, port: u16) -> thrift::Result<CalculatorSyncClient> {
+ let mut t = TTcpTransport::new();
+
+ // open the underlying TCP stream
+ println!("connecting to tutorial server on {}:{}", host, port);
+ let t = match t.open(&format!("{}:{}", host, port)) {
+ Ok(()) => t,
+ Err(e) => {
+ return Err(format!("failed to open tcp stream to {}:{} error:{:?}",
+ host,
+ port,
+ e)
+ .into());
+ }
+ };
+
+ // refcounted because it's shared by both input and output transports
+ let t = Rc::new(RefCell::new(Box::new(t) as Box<TTransport>));
+
+ // wrap a raw socket (slow) with a buffered transport of some kind
+ let t = Box::new(TFramedTransport::new(t)) as Box<TTransport>;
+
+ // refcounted again because it's shared by both input and output protocols
+ let t = Rc::new(RefCell::new(t));
+
+ // now create the protocol implementations
+ let i_prot = Box::new(TCompactInputProtocol::new(t.clone())) as Box<TInputProtocol>;
+ let o_prot = Box::new(TCompactOutputProtocol::new(t.clone())) as Box<TOutputProtocol>;
+
+ // we're done!
+ Ok(CalculatorSyncClient::new(i_prot, o_prot))
+}
diff --git a/tutorial/rs/src/bin/tutorial_server.rs b/tutorial/rs/src/bin/tutorial_server.rs
new file mode 100644
index 0000000..9cc1866
--- /dev/null
+++ b/tutorial/rs/src/bin/tutorial_server.rs
@@ -0,0 +1,168 @@
+// 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.
+
+#[macro_use]
+extern crate clap;
+
+extern crate thrift;
+extern crate thrift_tutorial;
+
+use std::collections::HashMap;
+use std::convert::{From, Into};
+use std::default::Default;
+
+use thrift::protocol::{TInputProtocolFactory, TOutputProtocolFactory};
+use thrift::protocol::{TCompactInputProtocolFactory, TCompactOutputProtocolFactory};
+use thrift::server::TSimpleServer;
+
+use thrift::transport::{TFramedTransportFactory, TTransportFactory};
+use thrift_tutorial::shared::{SharedServiceSyncHandler, SharedStruct};
+use thrift_tutorial::tutorial::{CalculatorSyncHandler, CalculatorSyncProcessor};
+use thrift_tutorial::tutorial::{InvalidOperation, Operation, Work};
+
+fn main() {
+ match run() {
+ Ok(()) => println!("tutorial server ran successfully"),
+ Err(e) => {
+ println!("tutorial server failed with error {:?}", e);
+ std::process::exit(1);
+ }
+ }
+}
+
+fn run() -> thrift::Result<()> {
+ let options = clap_app!(rust_tutorial_server =>
+ (version: "0.1.0")
+ (author: "Apache Thrift Developers <dev@thrift.apache.org>")
+ (about: "Thrift Rust tutorial server")
+ (@arg port: --port +takes_value "port on which the tutorial server listens")
+ );
+ let matches = options.get_matches();
+
+ let port = value_t!(matches, "port", u16).unwrap_or(9090);
+ let listen_address = format!("127.0.0.1:{}", port);
+
+ println!("binding to {}", listen_address);
+
+ let i_tran_fact: Box<TTransportFactory> = Box::new(TFramedTransportFactory::new());
+ let i_prot_fact: Box<TInputProtocolFactory> = Box::new(TCompactInputProtocolFactory::new());
+
+ let o_tran_fact: Box<TTransportFactory> = Box::new(TFramedTransportFactory::new());
+ let o_prot_fact: Box<TOutputProtocolFactory> = Box::new(TCompactOutputProtocolFactory::new());
+
+ // demux incoming messages
+ let processor = CalculatorSyncProcessor::new(CalculatorServer { ..Default::default() });
+
+ // create the server and start listening
+ let mut server = TSimpleServer::new(i_tran_fact,
+ i_prot_fact,
+ o_tran_fact,
+ o_prot_fact,
+ processor);
+
+ server.listen(&listen_address)
+}
+
+/// Handles incoming Calculator service calls.
+struct CalculatorServer {
+ log: HashMap<i32, SharedStruct>,
+}
+
+impl Default for CalculatorServer {
+ fn default() -> CalculatorServer {
+ CalculatorServer { log: HashMap::new() }
+ }
+}
+
+// since Calculator extends SharedService we have to implement the
+// handler for both traits.
+//
+
+// SharedService handler
+impl SharedServiceSyncHandler for CalculatorServer {
+ fn handle_get_struct(&mut self, key: i32) -> thrift::Result<SharedStruct> {
+ self.log
+ .get(&key)
+ .cloned()
+ .ok_or_else(|| format!("could not find log for key {}", key).into())
+ }
+}
+
+// Calculator handler
+impl CalculatorSyncHandler for CalculatorServer {
+ fn handle_ping(&mut self) -> thrift::Result<()> {
+ println!("pong!");
+ Ok(())
+ }
+
+ fn handle_add(&mut self, num1: i32, num2: i32) -> thrift::Result<i32> {
+ println!("handling add: n1:{} n2:{}", num1, num2);
+ Ok(num1 + num2)
+ }
+
+ fn handle_calculate(&mut self, logid: i32, w: Work) -> thrift::Result<i32> {
+ println!("handling calculate: l:{}, w:{:?}", logid, w);
+
+ let res = if let Some(ref op) = w.op {
+ if w.num1.is_none() || w.num2.is_none() {
+ Err(InvalidOperation {
+ what_op: Some(*op as i32),
+ why: Some("no operands specified".to_owned()),
+ })
+ } else {
+ // so that I don't have to call unwrap() multiple times below
+ let num1 = w.num1.as_ref().expect("operands checked");
+ let num2 = w.num2.as_ref().expect("operands checked");
+
+ match *op {
+ Operation::ADD => Ok(num1 + num2),
+ Operation::SUBTRACT => Ok(num1 - num2),
+ Operation::MULTIPLY => Ok(num1 * num2),
+ Operation::DIVIDE => {
+ if *num2 == 0 {
+ Err(InvalidOperation {
+ what_op: Some(*op as i32),
+ why: Some("divide by 0".to_owned()),
+ })
+ } else {
+ Ok(num1 / num2)
+ }
+ }
+ }
+ }
+ } else {
+ Err(InvalidOperation::new(None, "no operation specified".to_owned()))
+ };
+
+ // if the operation was successful log it
+ if let Ok(ref v) = res {
+ self.log.insert(logid, SharedStruct::new(logid, format!("{}", v)));
+ }
+
+ // the try! macro automatically maps errors
+ // but, since we aren't using that here we have to map errors manually
+ //
+ // exception structs defined in the IDL have an auto-generated
+ // impl of From::from
+ res.map_err(From::from)
+ }
+
+ fn handle_zip(&mut self) -> thrift::Result<()> {
+ println!("handling zip");
+ Ok(())
+ }
+}
diff --git a/tutorial/rs/src/lib.rs b/tutorial/rs/src/lib.rs
new file mode 100644
index 0000000..40007e5
--- /dev/null
+++ b/tutorial/rs/src/lib.rs
@@ -0,0 +1,23 @@
+// 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.
+
+extern crate ordered_float;
+extern crate thrift;
+extern crate try_from;
+
+pub mod shared;
+pub mod tutorial;