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