feat(grpc): Add basic grpc example

This commit is contained in:
Alexander Navarro 2024-04-23 20:12:18 -04:00
parent 3439bdc37a
commit 3bc2286586
7 changed files with 157 additions and 2 deletions

View file

@ -6,3 +6,9 @@ edition = "2021"
[dependencies] [dependencies]
clap = { version = "4.5.4", features = ["derive"] } clap = { version = "4.5.4", features = ["derive"] }
ignore = "0.4.22" ignore = "0.4.22"
prost = "0.12.4"
tokio = { version = "1", features = ["full"] }
tonic = "0.11.0"
[build-dependencies]
tonic-build = "0.11.0"

5
build.rs Normal file
View file

@ -0,0 +1,5 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/helloworld.proto")?;
Ok(())
}

15
proto/helloworld.proto Normal file
View file

@ -0,0 +1,15 @@
syntax = "proto3";
package helloworld;
service Greater {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}

37
src/grpc.rs Normal file
View file

@ -0,0 +1,37 @@
use std::error::Error;
use std::net::{SocketAddr, TcpListener};
use tonic::async_trait;
use self::client::GRPCClient;
use self::server::GRPCServer;
mod client;
mod server;
pub mod hello_world {
tonic::include_proto!("helloworld");
}
#[async_trait]
pub trait Connection {
async fn connect(&self) -> Result<(), Box<dyn Error>>;
}
fn is_socket_in_use(addr: String) -> bool {
let socket: SocketAddr = addr.parse().expect("Failed to create socket");
match TcpListener::bind(socket) {
Ok(_) => true,
Err(_) => false,
}
}
pub fn run() -> Result<Box<dyn Connection>, Box<dyn Error>> {
let addr = "[::1]:50051";
if is_socket_in_use(addr.to_string()) {
Ok(Box::new(GRPCServer::new(addr.to_string())))
} else {
Ok(Box::new(GRPCClient::new(addr.to_string())))
}
}

33
src/grpc/client.rs Normal file
View file

@ -0,0 +1,33 @@
use super::hello_world;
use hello_world::greater_client::GreaterClient;
use hello_world::HelloRequest;
use tonic::async_trait;
#[derive(Debug, Default)]
pub struct GRPCClient {
address: String,
}
impl GRPCClient {
pub fn new(address: String) -> Self {
Self { address }
}
}
#[async_trait]
impl super::Connection for GRPCClient {
async fn connect(&self) -> Result<(), Box<dyn std::error::Error>> {
let mut client = GreaterClient::connect(format!("http://{}", self.address)).await?;
let request = tonic::Request::new(HelloRequest {
name: "Self".into(),
});
let response = client.say_hello(request).await?;
println!("RESPONSE={:?}", response);
Ok(())
}
}

50
src/grpc/server.rs Normal file
View file

@ -0,0 +1,50 @@
use super::hello_world;
use hello_world::greater_server::{Greater, GreaterServer};
use hello_world::{HelloRequest, HelloResponse};
use std::error::Error;
use std::net::SocketAddr;
use tonic::transport::Server;
use tonic::{async_trait, Request, Response, Result, Status};
#[derive(Debug, Default)]
pub struct GRPCServer {
address: String,
}
impl GRPCServer {
pub fn new(address: String) -> Self {
Self { address }
}
}
#[tonic::async_trait]
impl Greater for GRPCServer {
async fn say_hello(
&self,
request: Request<HelloRequest>,
) -> Result<Response<HelloResponse>, Status> {
println!("Got a request {:?}", request);
let reply = hello_world::HelloResponse {
message: format!("Hello {}!", request.into_inner().name),
};
Ok(Response::new(reply))
}
}
#[async_trait]
impl super::Connection for GRPCServer {
async fn connect(&self) -> Result<(), Box<dyn Error>> {
println!("Starting server on: \"{}\"", self.address);
let socket: SocketAddr = self.address.parse()?;
Server::builder()
.add_service(GreaterServer::new(GRPCServer::default()))
.serve(socket)
.await?;
Ok(())
}
}

View file

@ -1,8 +1,10 @@
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
use clap::Parser; use clap::Parser;
use std::error::Error;
mod file_explorer; mod file_explorer;
mod grpc;
#[derive(Parser)] #[derive(Parser)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
@ -11,7 +13,8 @@ struct Args {
path: Option<PathBuf>, path: Option<PathBuf>,
} }
fn main() { #[tokio::main()]
async fn main() -> Result<(), Box<dyn Error>> {
let cli = Args::parse(); let cli = Args::parse();
let path = cli let path = cli
.path .path
@ -19,5 +22,11 @@ fn main() {
let files = file_explorer::walk_dir(&path).expect("error"); let files = file_explorer::walk_dir(&path).expect("error");
eprintln!("DEBUGPRINT[4]: main.rs:20: files={:#?}", files); eprintln!("DEBUGPRINT[4]: main.rs:20: files={:#?}", files.len());
let server = grpc::run()?;
server.connect().await?;
Ok(())
} }