aocrun/
lib.rs

1//! A gRPC server using [`mod@tonic`] to serve [`Aoc`](https://adventofcode.com) answer requests.
2//!
3//! # Protobuf
4//! ```text
5//! syntax = "proto3";
6//! package aocrunner;
7//! 
8//! service AocRun {
9//! 	rpc GetAnswer(Attributes) returns (Answer);
10//! }
11//! 
12//! message Attributes {
13//! 	uint32 year = 1;
14//! 	uint32 day = 2;
15//! 	optional string input = 3;
16//! }
17//! 
18//! message Answer {
19//! 	string part_one = 1;
20//! 	string part_two = 2;
21//! }
22//! ```
23//!
24//! # Examples
25//! Assuming Aocrun server is listening to localhost port `41470`. Using [`grpcurl`](https://github.com/fullstorydev/grpcurl),
26//!
27//! ```text
28//! $ grpcurl -plaintext -import-path ./proto \
29//!   -proto aocrunner.proto \
30//!   -d '{"year": 2022, "day": 1, "input": "1000\\n2000\\n\\n1500\\n2500\\n4000"}' '[::1]:41470' \
31//!   aocrunner.AocRun/GetAnswer
32//! ```
33//!
34//! The response received is
35//! ```text
36//! {
37//!   "partOne": "8000",
38//!   "partTwo": "11000"
39//! }
40//! ```
41
42mod service;
43
44use aocdata::AocDataClient;
45use aocrunner::aoc_run_server::AocRunServer;
46use service::AocRunService;
47use tonic::transport::{Channel, Server};
48
49mod aocrunner {
50    tonic::include_proto!("aocrunner");
51}
52
53/// gRPC stub to create client.
54pub use aocrunner::aoc_run_client::AocRunClient;
55
56/// gRPC stub to create solve request
57pub use aocrunner::Attributes;
58
59/// gRPC stub to destructure answer
60pub use aocrunner::Answer;
61
62const AOC_DATA_URL_ENV: &str = "AOC_DATA_URL";
63const AOC_RUN_PORT_ENV: &str = "AOC_RUN_PORT";
64const AOC_RUN_DEFAULT_PORT: u32 = 4147;
65
66/// Connect to AocData using AocDataClient, and start AocRun service to handle gRPC requests.
67pub async fn run() -> Result<(), Box<dyn std::error::Error>> {
68    let data_client = connect_data_service().await?;
69
70    start_runner_service(data_client).await?;
71
72    Ok(())
73}
74
75async fn connect_data_service() -> Result<AocDataClient<Channel>, Box<dyn std::error::Error>> {
76    let data_url = std::env::var(AOC_DATA_URL_ENV).map_err(|err| {
77        format!(
78            "environment variable {} must be provided: {}",
79            AOC_DATA_URL_ENV, err
80        )
81    })?;
82
83    let data_client = AocDataClient::connect(data_url).await?;
84
85    Ok(data_client)
86}
87
88async fn start_runner_service(
89    data_client: AocDataClient<Channel>,
90) -> Result<(), Box<dyn std::error::Error>> {
91    let port = match std::env::var(AOC_RUN_PORT_ENV) {
92        Ok(port) => port.parse()?,
93        Err(_) => AOC_RUN_DEFAULT_PORT,
94    };
95
96    let addr = format!("[::]:{}", port).parse()?;
97    let aoc_run = AocRunService::new(data_client);
98    let svc = AocRunServer::new(aoc_run);
99
100    Server::builder().add_service(svc).serve(addr).await?;
101
102    Ok(())
103}