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