aocdata 0.1.0

gRPC server interface to database that serves AOC puzzle dataset requests.
Documentation
//! A gRPC server using [`tonic`] to serve AOC data requests.
//!
//! [`tonic`]: https://docs.rs/tonic/0.9.2/tonic/

use aocdata::aoc_data_server::AocDataServer;
use sea_orm::Database;
use sea_orm::DatabaseConnection;
use service::AocDataService;
use tonic::transport::Server;

mod entities;
mod service;

mod aocdata {
    tonic::include_proto!("aocdata");
}

/// gRPC stub to create client.
pub use aocdata::aoc_data_client::AocDataClient;

/// gRPC stub to create data request.
pub use aocdata::DataRequest;

/// gRPC stub to destructure data response.
pub use aocdata::DataResponse;

const AOC_DATA_PORT_ENV: &str = "AOC_DATA_PORT";
const AOC_DB_URL_ENV: &str = "AOC_DB_URL";

/// To start the gRPC server. By default, listens to requests at port 3137.
pub async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let connection = connect_db().await?;

    start_data_service(connection).await?;

    Ok(())
}

async fn connect_db() -> Result<DatabaseConnection, Box<dyn std::error::Error + Send + Sync>> {
    let url = std::env::var(AOC_DB_URL_ENV).map_err(|err| {
        format!(
            "environment variable {} must be provided: {}",
            AOC_DB_URL_ENV, err
        )
    })?;

    let connection = Database::connect(url)
        .await
        .map_err(|err| format!("failed to connect to database: {}", err))?;

    Ok(connection)
}

async fn start_data_service(
    connection: DatabaseConnection,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let port = match std::env::var(AOC_DATA_PORT_ENV) {
        Ok(port) => port.parse()?,
        Err(_) => 3137,
    };

    let addr = format!("[::]:{}", port).parse()?;

    let data = AocDataService::with_db_conn(connection);

    let svc = AocDataServer::new(data);

    Server::builder().add_service(svc).serve(addr).await?;

    Ok(())
}