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