Skip to main content

allwright/
client_runtime.rs

1use std::sync::{Arc, Mutex, OnceLock};
2
3use crate::proto::engine_service_client::EngineServiceClient;
4
5use super::types::{Error, Result, RuntimeClient};
6
7const DEFAULT_SERVER_ADDR: &str = "http://127.0.0.1:50051";
8const SERVER_ADDR_ENV_VAR: &str = "ALLWRIGHT_SERVER_ADDR";
9
10static RUNTIME: OnceLock<Mutex<Option<Arc<RuntimeClient>>>> = OnceLock::new();
11static SERVER_ADDR_OVERRIDE: OnceLock<Mutex<Option<String>>> = OnceLock::new();
12
13pub async fn ping() -> Result<String> {
14    let runtime = get_runtime().await?;
15    let mut engine = runtime.engine.clone();
16    let response = engine.ping(tonic::Request::new(crate::proto::PingRequest {})).await?;
17    Ok(response.into_inner().message)
18}
19
20pub fn set_server_addr(server_addr: impl Into<String>) -> Result<()> {
21    let normalized = normalize_server_addr(&server_addr.into());
22    let mut override_slot = server_addr_override_slot()
23        .lock()
24        .map_err(|_| Error::new("server address override lock is poisoned"))?;
25    *override_slot = Some(normalized);
26    drop(override_slot);
27
28    let mut runtime = runtime_slot()
29        .lock()
30        .map_err(|_| Error::new("runtime singleton lock is poisoned"))?;
31    *runtime = None;
32    Ok(())
33}
34
35pub async fn shutdown() {
36    if let Ok(mut runtime) = runtime_slot().lock() {
37        *runtime = None;
38    }
39}
40
41pub(crate) async fn get_runtime() -> Result<Arc<RuntimeClient>> {
42    if let Ok(runtime) = runtime_slot().lock() {
43        if let Some(existing) = runtime.as_ref() {
44            return Ok(Arc::clone(existing));
45        }
46    }
47
48    let endpoint = configured_server_addr();
49    let engine = EngineServiceClient::connect(endpoint).await?;
50    let runtime = Arc::new(RuntimeClient { engine });
51
52    let mut slot = runtime_slot()
53        .lock()
54        .map_err(|_| Error::new("runtime singleton lock is poisoned"))?;
55    if let Some(existing) = slot.as_ref() {
56        return Ok(Arc::clone(existing));
57    }
58    *slot = Some(Arc::clone(&runtime));
59    Ok(runtime)
60}
61
62fn runtime_slot() -> &'static Mutex<Option<Arc<RuntimeClient>>> {
63    RUNTIME.get_or_init(|| Mutex::new(None))
64}
65
66fn server_addr_override_slot() -> &'static Mutex<Option<String>> {
67    SERVER_ADDR_OVERRIDE.get_or_init(|| Mutex::new(None))
68}
69
70fn configured_server_addr() -> String {
71    if let Ok(server_addr_override) = server_addr_override_slot().lock() {
72        if let Some(server_addr) = server_addr_override.as_ref() {
73            return server_addr.clone();
74        }
75    }
76
77    normalize_server_addr(
78        std::env::var(SERVER_ADDR_ENV_VAR)
79            .ok()
80            .filter(|value| !value.trim().is_empty())
81            .as_deref()
82            .unwrap_or(DEFAULT_SERVER_ADDR),
83    )
84}
85
86fn normalize_server_addr(raw: &str) -> String {
87    let trimmed = raw.trim();
88    if trimmed.starts_with("http://") || trimmed.starts_with("https://") {
89        trimmed.to_string()
90    } else {
91        format!("http://{trimmed}")
92    }
93}