subliminal-protos-rust 0.0.1

Rust Protobuf generation for Subliminal Microservices Project
Documentation
pub mod errors;

use broker::broker_client::BrokerClient;
use db::db_handler_client::DbHandlerClient;
use log::info;
use service_registry::{service_registry_client::ServiceRegistryClient, GetServiceRequest};
use tonic::transport::Channel;
use ulid::{DecodeError, Ulid};

#[path = "codegen/db.rs"]
pub mod db;

#[path = "codegen/service_registry.rs"]
pub mod service_registry;

#[path = "codegen/broker.rs"]
pub mod broker;

/// Get a datastore client using the provided service registry channel.
/// Will return a tonic::NotFound error if the datastore is not registered in the service registry.
/// Will return a tonic::Internal error if the service registry returns an unexpected error.
/// Will return a tonic::Unavailable error if the datastore cannot be reached.
pub async fn get_datastore_client(
    service_registry_channel: Channel,
) -> Result<DbHandlerClient<Channel>, tonic::Status> {
    // Build the service registry client from the provided channel
    let mut service_registry_client: ServiceRegistryClient<Channel> =
        ServiceRegistryClient::new(service_registry_channel);

    // Get the database handler address from the provided service registry
    let datastore_url = match service_registry_client
        .get_service(GetServiceRequest {
            service_name: String::from("db"),
        })
        .await
    {
        Ok(response) => {
            let resp = response.into_inner();
            format!("http://{}:{}", resp.service_address, resp.service_port)
        }
        // We don't want to just pass any error along (because clients don't need nor want to handle every case),
        // Instead, we filter the error and return a more concise error type/message so it's easier for the client to handle
        Err(e) => match e.code() {
            tonic::Code::NotFound => {
                return Err(tonic::Status::new(
                    tonic::Code::NotFound,
                    "Message broker not found in service registry",
                ))
            }
            _ => return Err(tonic::Status::new(tonic::Code::Internal, e.to_string())),
        },
    };

    // Using the address, connect to the database handler and return the client
    DbHandlerClient::connect(datastore_url)
        .await
        .map_err(|e| tonic::Status::new(tonic::Code::Unavailable, e.to_string()))
}

/// Get a message broker client using the provided service registry channel.
/// Will return a tonic::NotFound error if the message broker is not registered in the service registry.
/// Will return a tonic::Internal error if the service registry returns an unexpected error.
/// Will return a tonic::Unavailable error if the message broker cannot be reached.
pub async fn get_message_broker_client(
    message_broker_channel: Channel,
) -> Result<BrokerClient<Channel>, tonic::Status> {
    // Build the service registry client from the provided channel
    let mut service_registry_client: ServiceRegistryClient<Channel> =
        ServiceRegistryClient::new(message_broker_channel);

    // Get the message broker address from the provided service registry
    let message_broker_url = match service_registry_client
        .get_service(GetServiceRequest {
            service_name: String::from("message_broker"),
        })
        .await
    {
        Ok(response) => {
            let resp = response.into_inner();
            format!("http://{}:{}", resp.service_address, resp.service_port)
        }
        // We don't want to just pass any error along (because clients don't need nor want to handle every case),
        // Instead, we filter the error and return a more concise error type/message so it's easier for the client to handle
        Err(e) => match e.code() {
            tonic::Code::NotFound => {
                return Err(tonic::Status::new(
                    tonic::Code::NotFound,
                    "Message broker not found in service registry",
                ))
            }
            _ => return Err(tonic::Status::new(tonic::Code::Internal, e.to_string())),
        },
    };

    // Using the address, connect to the message broker and return the client
    BrokerClient::connect(message_broker_url)
        .await
        .map_err(|e| tonic::Status::new(tonic::Code::Unavailable, e.to_string()))
}

/// Quick helper function to verify that a ULID is valid.
/// We need this because the ULID type does not implement the JsonSchema trait that
/// would allow us to validate the ULID in the request directly. We don't need
/// to return the ULID itself since Redis just uses the string representation.
pub fn parse_ulid(id: &str) -> Result<Ulid, DecodeError> {
    let res = id.parse::<Ulid>();
    match res {
        Ok(ulid) => Ok(ulid),
        Err(e) => {
            info!("Error parsing ULID: {}", e);
            Err(e)
        }
    }
}