summaryrefslogtreecommitdiff
path: root/tutorial/rs/src/bin/tutorial_server.rs
blob: ab6df57fb06ab261532bd32f43b41ca26a61e7ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// 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 std::collections::HashMap;
use std::convert::{From, Into};
use std::default::Default;
use std::sync::Mutex;

use clap::{clap_app, value_t};

use thrift::protocol::{TCompactInputProtocolFactory, TCompactOutputProtocolFactory};
use thrift::server::TServer;
use thrift::transport::{TFramedReadTransportFactory, TFramedWriteTransportFactory};

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 = TFramedReadTransportFactory::new();
    let i_prot_fact = TCompactInputProtocolFactory::new();

    let o_tran_fact = TFramedWriteTransportFactory::new();
    let o_prot_fact = TCompactOutputProtocolFactory::new();

    // demux incoming messages
    let processor = CalculatorSyncProcessor::new(CalculatorServer {
        ..Default::default()
    });

    // create the server and start listening
    let mut server = TServer::new(
        i_tran_fact,
        i_prot_fact,
        o_tran_fact,
        o_prot_fact,
        processor,
        10,
    );

    server.listen(&listen_address)
}

/// Handles incoming Calculator service calls.
struct CalculatorServer {
    log: Mutex<HashMap<i32, SharedStruct>>,
}

impl Default for CalculatorServer {
    fn default() -> CalculatorServer {
        CalculatorServer {
            log: Mutex::new(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(&self, key: i32) -> thrift::Result<SharedStruct> {
        let log = self.log.lock().unwrap();
        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(&self) -> thrift::Result<()> {
        println!("pong!");
        Ok(())
    }

    fn handle_add(&self, num1: i32, num2: i32) -> thrift::Result<i32> {
        println!("handling add: n1:{} n2:{}", num1, num2);
        Ok(num1 + num2)
    }

    fn handle_calculate(&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.into()),
                    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.into()),
                                why: Some("divide by 0".to_owned()),
                            })
                        } else {
                            Ok(num1 / num2)
                        }
                    }
                    _ => {
                        let op_val: i32 = op.into();
                        Err(InvalidOperation {
                            what_op: Some(op_val),
                            why: Some(format!("unsupported operation type '{}'", op_val)),
                        })
                    }
                }
            }
        } else {
            Err(InvalidOperation::new(
                None,
                "no operation specified".to_owned(),
            ))
        };

        // if the operation was successful log it
        if let Ok(ref v) = res {
            let mut log = self.log.lock().unwrap();
            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(&self) -> thrift::Result<()> {
        println!("handling zip");
        Ok(())
    }
}