diff --git a/Cargo.toml b/Cargo.toml index be46c38..ad55fd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,9 @@ edition = "2021" [dependencies] clap = { version = "4.5.4", features = ["derive"] } ignore = "0.4.22" +prost = "0.12.4" +tokio = { version = "1", features = ["full"] } +tonic = "0.11.0" + +[build-dependencies] +tonic-build = "0.11.0" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..4976509 --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +fn main() -> Result<(), Box> { + tonic_build::compile_protos("proto/helloworld.proto")?; + + Ok(()) +} diff --git a/proto/helloworld.proto b/proto/helloworld.proto new file mode 100644 index 0000000..519a530 --- /dev/null +++ b/proto/helloworld.proto @@ -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; +} diff --git a/src/grpc.rs b/src/grpc.rs new file mode 100644 index 0000000..f73f423 --- /dev/null +++ b/src/grpc.rs @@ -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>; +} + +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> { + 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()))) + } +} diff --git a/src/grpc/client.rs b/src/grpc/client.rs new file mode 100644 index 0000000..d652274 --- /dev/null +++ b/src/grpc/client.rs @@ -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> { + 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(()) + } +} diff --git a/src/grpc/server.rs b/src/grpc/server.rs new file mode 100644 index 0000000..1590ef6 --- /dev/null +++ b/src/grpc/server.rs @@ -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, + ) -> Result, 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> { + println!("Starting server on: \"{}\"", self.address); + + let socket: SocketAddr = self.address.parse()?; + + Server::builder() + .add_service(GreaterServer::new(GRPCServer::default())) + .serve(socket) + .await?; + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 749161a..167b791 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ use std::{env, path::PathBuf}; use clap::Parser; +use std::error::Error; mod file_explorer; +mod grpc; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -11,7 +13,8 @@ struct Args { path: Option, } -fn main() { +#[tokio::main()] +async fn main() -> Result<(), Box> { let cli = Args::parse(); let path = cli .path @@ -19,5 +22,11 @@ fn main() { 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(()) }