xref: /tonic/examples/src/routeguide/server.rs (revision b2e56654)
1 use std::collections::HashMap;
2 use std::pin::Pin;
3 use std::sync::Arc;
4 use std::time::Instant;
5 
6 use tokio::sync::mpsc;
7 use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt};
8 use tonic::transport::Server;
9 use tonic::{Request, Response, Status};
10 
11 use routeguide::route_guide_server::{RouteGuide, RouteGuideServer};
12 use routeguide::{Feature, Point, Rectangle, RouteNote, RouteSummary};
13 
14 pub mod routeguide {
15     tonic::include_proto!("routeguide");
16 }
17 
18 mod data;
19 
20 #[derive(Debug)]
21 pub struct RouteGuideService {
22     features: Arc<Vec<Feature>>,
23 }
24 
25 #[tonic::async_trait]
26 impl RouteGuide for RouteGuideService {
get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status>27     async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
28         println!("GetFeature = {:?}", request);
29 
30         for feature in &self.features[..] {
31             if feature.location.as_ref() == Some(request.get_ref()) {
32                 return Ok(Response::new(feature.clone()));
33             }
34         }
35 
36         Ok(Response::new(Feature::default()))
37     }
38 
39     type ListFeaturesStream = ReceiverStream<Result<Feature, Status>>;
40 
list_features( &self, request: Request<Rectangle>, ) -> Result<Response<Self::ListFeaturesStream>, Status>41     async fn list_features(
42         &self,
43         request: Request<Rectangle>,
44     ) -> Result<Response<Self::ListFeaturesStream>, Status> {
45         println!("ListFeatures = {:?}", request);
46 
47         let (tx, rx) = mpsc::channel(4);
48         let features = self.features.clone();
49 
50         tokio::spawn(async move {
51             for feature in &features[..] {
52                 if in_range(feature.location.as_ref().unwrap(), request.get_ref()) {
53                     println!("  => send {:?}", feature);
54                     tx.send(Ok(feature.clone())).await.unwrap();
55                 }
56             }
57 
58             println!(" /// done sending");
59         });
60 
61         Ok(Response::new(ReceiverStream::new(rx)))
62     }
63 
record_route( &self, request: Request<tonic::Streaming<Point>>, ) -> Result<Response<RouteSummary>, Status>64     async fn record_route(
65         &self,
66         request: Request<tonic::Streaming<Point>>,
67     ) -> Result<Response<RouteSummary>, Status> {
68         println!("RecordRoute");
69 
70         let mut stream = request.into_inner();
71 
72         let mut summary = RouteSummary::default();
73         let mut last_point = None;
74         let now = Instant::now();
75 
76         while let Some(point) = stream.next().await {
77             let point = point?;
78 
79             println!("  ==> Point = {:?}", point);
80 
81             // Increment the point count
82             summary.point_count += 1;
83 
84             // Find features
85             for feature in &self.features[..] {
86                 if feature.location.as_ref() == Some(&point) {
87                     summary.feature_count += 1;
88                 }
89             }
90 
91             // Calculate the distance
92             if let Some(ref last_point) = last_point {
93                 summary.distance += calc_distance(last_point, &point);
94             }
95 
96             last_point = Some(point);
97         }
98 
99         summary.elapsed_time = now.elapsed().as_secs() as i32;
100 
101         Ok(Response::new(summary))
102     }
103 
104     type RouteChatStream = Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + 'static>>;
105 
route_chat( &self, request: Request<tonic::Streaming<RouteNote>>, ) -> Result<Response<Self::RouteChatStream>, Status>106     async fn route_chat(
107         &self,
108         request: Request<tonic::Streaming<RouteNote>>,
109     ) -> Result<Response<Self::RouteChatStream>, Status> {
110         println!("RouteChat");
111 
112         let mut notes = HashMap::new();
113         let mut stream = request.into_inner();
114 
115         let output = async_stream::try_stream! {
116             while let Some(note) = stream.next().await {
117                 let note = note?;
118 
119                 let location = note.location.unwrap();
120 
121                 let location_notes = notes.entry(location).or_insert(vec![]);
122                 location_notes.push(note);
123 
124                 for note in location_notes {
125                     yield note.clone();
126                 }
127             }
128         };
129 
130         Ok(Response::new(Box::pin(output) as Self::RouteChatStream))
131     }
132 }
133 
134 #[tokio::main]
main() -> Result<(), Box<dyn std::error::Error>>135 async fn main() -> Result<(), Box<dyn std::error::Error>> {
136     let addr = "[::1]:10000".parse().unwrap();
137 
138     println!("RouteGuideServer listening on: {}", addr);
139 
140     let route_guide = RouteGuideService {
141         features: Arc::new(data::load()),
142     };
143 
144     let svc = RouteGuideServer::new(route_guide);
145 
146     Server::builder().add_service(svc).serve(addr).await?;
147 
148     Ok(())
149 }
150 
151 impl Eq for Point {}
152 
in_range(point: &Point, rect: &Rectangle) -> bool153 fn in_range(point: &Point, rect: &Rectangle) -> bool {
154     use std::cmp;
155 
156     let lo = rect.lo.as_ref().unwrap();
157     let hi = rect.hi.as_ref().unwrap();
158 
159     let left = cmp::min(lo.longitude, hi.longitude);
160     let right = cmp::max(lo.longitude, hi.longitude);
161     let top = cmp::max(lo.latitude, hi.latitude);
162     let bottom = cmp::min(lo.latitude, hi.latitude);
163 
164     point.longitude >= left
165         && point.longitude <= right
166         && point.latitude >= bottom
167         && point.latitude <= top
168 }
169 
170 /// Calculates the distance between two points using the "haversine" formula.
171 /// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
calc_distance(p1: &Point, p2: &Point) -> i32172 fn calc_distance(p1: &Point, p2: &Point) -> i32 {
173     const CORD_FACTOR: f64 = 1e7;
174     const R: f64 = 6_371_000.0; // meters
175 
176     let lat1 = p1.latitude as f64 / CORD_FACTOR;
177     let lat2 = p2.latitude as f64 / CORD_FACTOR;
178     let lng1 = p1.longitude as f64 / CORD_FACTOR;
179     let lng2 = p2.longitude as f64 / CORD_FACTOR;
180 
181     let lat_rad1 = lat1.to_radians();
182     let lat_rad2 = lat2.to_radians();
183 
184     let delta_lat = (lat2 - lat1).to_radians();
185     let delta_lng = (lng2 - lng1).to_radians();
186 
187     let a = (delta_lat / 2f64).sin() * (delta_lat / 2f64).sin()
188         + (lat_rad1).cos() * (lat_rad2).cos() * (delta_lng / 2f64).sin() * (delta_lng / 2f64).sin();
189 
190     let c = 2f64 * a.sqrt().atan2((1f64 - a).sqrt());
191 
192     (R * c) as i32
193 }
194