1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
16 // under the License.
17
18 use std::collections::HashMap;
19 use std::convert::{From, Into};
20 use std::default::Default;
21 use std::sync::Mutex;
22
23 use clap::{clap_app, value_t};
24
25 use thrift::protocol::{TCompactInputProtocolFactory, TCompactOutputProtocolFactory};
26 use thrift::server::TServer;
27 use thrift::transport::{TFramedReadTransportFactory, TFramedWriteTransportFactory};
28
29 use thrift_tutorial::shared::{SharedServiceSyncHandler, SharedStruct};
30 use thrift_tutorial::tutorial::{CalculatorSyncHandler, CalculatorSyncProcessor};
31 use thrift_tutorial::tutorial::{InvalidOperation, Operation, Work};
32
main()33 fn main() {
34 match run() {
35 Ok(()) => println!("tutorial server ran successfully"),
36 Err(e) => {
37 println!("tutorial server failed with error {:?}", e);
38 std::process::exit(1);
39 }
40 }
41 }
42
run() -> thrift::Result<()>43 fn run() -> thrift::Result<()> {
44 let options = clap_app!(rust_tutorial_server =>
45 (version: "0.1.0")
46 (author: "Apache Thrift Developers <dev@thrift.apache.org>")
47 (about: "Thrift Rust tutorial server")
48 (@arg port: --port +takes_value "port on which the tutorial server listens")
49 );
50 let matches = options.get_matches();
51
52 let port = value_t!(matches, "port", u16).unwrap_or(9090);
53 let listen_address = format!("127.0.0.1:{}", port);
54
55 println!("binding to {}", listen_address);
56
57 let i_tran_fact = TFramedReadTransportFactory::new();
58 let i_prot_fact = TCompactInputProtocolFactory::new();
59
60 let o_tran_fact = TFramedWriteTransportFactory::new();
61 let o_prot_fact = TCompactOutputProtocolFactory::new();
62
63 // demux incoming messages
64 let processor = CalculatorSyncProcessor::new(CalculatorServer {
65 ..Default::default()
66 });
67
68 // create the server and start listening
69 let mut server = TServer::new(
70 i_tran_fact,
71 i_prot_fact,
72 o_tran_fact,
73 o_prot_fact,
74 processor,
75 10,
76 );
77
78 server.listen(&listen_address)
79 }
80
81 /// Handles incoming Calculator service calls.
82 struct CalculatorServer {
83 log: Mutex<HashMap<i32, SharedStruct>>,
84 }
85
86 impl Default for CalculatorServer {
default() -> CalculatorServer87 fn default() -> CalculatorServer {
88 CalculatorServer {
89 log: Mutex::new(HashMap::new()),
90 }
91 }
92 }
93
94 // since Calculator extends SharedService we have to implement the
95 // handler for both traits.
96 //
97
98 // SharedService handler
99 impl SharedServiceSyncHandler for CalculatorServer {
handle_get_struct(&self, key: i32) -> thrift::Result<SharedStruct>100 fn handle_get_struct(&self, key: i32) -> thrift::Result<SharedStruct> {
101 let log = self.log.lock().unwrap();
102 log.get(&key)
103 .cloned()
104 .ok_or_else(|| format!("could not find log for key {}", key).into())
105 }
106 }
107
108 // Calculator handler
109 impl CalculatorSyncHandler for CalculatorServer {
handle_ping(&self) -> thrift::Result<()>110 fn handle_ping(&self) -> thrift::Result<()> {
111 println!("pong!");
112 Ok(())
113 }
114
handle_add(&self, num1: i32, num2: i32) -> thrift::Result<i32>115 fn handle_add(&self, num1: i32, num2: i32) -> thrift::Result<i32> {
116 println!("handling add: n1:{} n2:{}", num1, num2);
117 Ok(num1 + num2)
118 }
119
handle_calculate(&self, logid: i32, w: Work) -> thrift::Result<i32>120 fn handle_calculate(&self, logid: i32, w: Work) -> thrift::Result<i32> {
121 println!("handling calculate: l:{}, w:{:?}", logid, w);
122
123 let res = if let Some(ref op) = w.op {
124 if w.num1.is_none() || w.num2.is_none() {
125 Err(InvalidOperation {
126 what_op: Some(op.into()),
127 why: Some("no operands specified".to_owned()),
128 })
129 } else {
130 // so that I don't have to call unwrap() multiple times below
131 let num1 = w.num1.as_ref().expect("operands checked");
132 let num2 = w.num2.as_ref().expect("operands checked");
133
134 match *op {
135 Operation::ADD => Ok(num1 + num2),
136 Operation::SUBTRACT => Ok(num1 - num2),
137 Operation::MULTIPLY => Ok(num1 * num2),
138 Operation::DIVIDE => {
139 if *num2 == 0 {
140 Err(InvalidOperation {
141 what_op: Some(op.into()),
142 why: Some("divide by 0".to_owned()),
143 })
144 } else {
145 Ok(num1 / num2)
146 }
147 }
148 _ => {
149 let op_val: i32 = op.into();
150 Err(InvalidOperation {
151 what_op: Some(op_val),
152 why: Some(format!("unsupported operation type '{}'", op_val)),
153 })
154 }
155 }
156 }
157 } else {
158 Err(InvalidOperation::new(
159 None,
160 "no operation specified".to_owned(),
161 ))
162 };
163
164 // if the operation was successful log it
165 if let Ok(ref v) = res {
166 let mut log = self.log.lock().unwrap();
167 log.insert(logid, SharedStruct::new(logid, format!("{}", v)));
168 }
169
170 // the try! macro automatically maps errors
171 // but, since we aren't using that here we have to map errors manually
172 //
173 // exception structs defined in the IDL have an auto-generated
174 // impl of From::from
175 res.map_err(From::from)
176 }
177
handle_zip(&self) -> thrift::Result<()>178 fn handle_zip(&self) -> thrift::Result<()> {
179 println!("handling zip");
180 Ok(())
181 }
182 }
183