Skip to content

Instantly share code, notes, and snippets.

@shantanoo-desai
Last active February 27, 2023 09:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shantanoo-desai/59fb65362b819dd99611d249ccd95666 to your computer and use it in GitHub Desktop.
Save shantanoo-desai/59fb65362b819dd99611d249ccd95666 to your computer and use it in GitHub Desktop.
gRPC using Tonic in Rust (example from gRPC: Up and Running Book)
syntax = "proto3";
package ecommerce;
service ProductInfo {
rpc addProduct(Product) returns (ProductID);
rpc getProduct(ProductID) returns (Product);
}
message Product {
string id = 1;
string name = 2;
string description = 3;
}
message ProductID {
string value = 1;
}
use tonic::{transport::Server, Request, Response, Status};
use uuid::Uuid;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
/**
* Generated gRPC Stub
*/
// Stub Import Syntax: <package_name>::<service_name_server>::{ServiceName, ServiceNameServer};
use ecommerce::product_info_server::{ProductInfo, ProductInfoServer};
// Stub Import Syntax: <package_name>::<Exact_Name_of_Message_In_Proto_File>
use ecommerce::{Product, ProductId};
mod ecommerce;
/**
* In-Memory Temporary DB
*/
#[derive(Clone, Debug, Default)]
pub struct InternalProduct {
name: String,
description: String,
}
#[derive(Clone, Debug, Default)]
pub struct Db {
shared: Arc<Shared>,
}
#[derive(Debug, Default)]
struct Shared {
products: Mutex<HashMap<String, InternalProduct>>
}
impl Db {
pub fn new() -> Self {
Self {
shared: Arc::new(Shared {
products: Mutex::new(HashMap::new())
})
}
}
pub fn insert(&self, key: String, prod: InternalProduct) {
let mut products = self.shared.products.lock().unwrap();
products.insert(key, prod);
}
pub fn get(&self, key: String) -> Option<(String, InternalProduct)> {
let products = self.shared.products.lock().unwrap();
match products.get(&key) {
Some(product) => Some((key, product.clone())),
None => None,
}
}
}
// Implement Service Skeleton for the "ProductInfo" Service
// Defined in the Proto File
#[derive(Debug, Default)]
pub struct MyProductInfo {
product_map: Db,
}
// Implement the service functions defined in the Proto File
// addProduct, getProduct Service
#[tonic::async_trait]
impl ProductInfo for MyProductInfo {
async fn add_product(&self, request: Request<Product>) -> Result<Response<ProductId>, Status> {
println!("Received Request: {:?}", request.remote_addr());
// generate a new UUID v4
let new_id = Uuid::new_v4();
// Extract the Incoming User Data of the Product
let mut new_product = request.into_inner();
// Replace the ID of the Product with generated UUID
new_product.id = new_id.clone().to_string();
let internal_product = InternalProduct { name: new_product.name, description: new_product.description };
self.product_map.insert(new_product.id, internal_product);
let response = ecommerce::ProductId { value: new_id.clone().to_string()};
Ok(Response::new(response))
}
async fn get_product(&self, request: Request<ProductId>) -> Result<Response<Product>, Status> {
println!("Received Request: {:?}", request.remote_addr());
let search_result = self.product_map.get(request.into_inner().value);
match search_result {
Some(found_product) => {
Ok(
Response::new(
ecommerce::Product {
id: found_product.0,
name: found_product.1.clone().name,
description: found_product.1.clone().description,
}
)
)
},
None => { Err(Status::not_found("Product Not Found")) }
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse()?;
let ecomm_service = MyProductInfo::default();
println!("Starting gRPC Server....");
Server::builder()
.add_service(ProductInfoServer::new(ecomm_service))
.serve(addr)
.await?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment