text/x-rust
•
3.51 KB
•
110 lines
use std::{
any::Any,
fs,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
time::Instant,
};
use anyhow::Result;
use rubhub_auth_store::{AuthStore, RunnerToken};
use tokio::sync::{OnceCell, broadcast};
use super::{AppConfig, RepoEvent};
use super::repo_service::RepoService;
use super::runner::RunnerRegistry;
/// Type-erased CI job sender (allows main crate to set specific sender type)
type CiJobSender = Box<dyn Any + Send + Sync>;
/// Global application state
#[derive(Debug, Clone)]
pub struct GlobalState {
pub auth: Arc<AuthStore>,
pub config: Arc<AppConfig>,
pub repo: Arc<RepoService>,
pub process_start: Instant,
pub event_tx: broadcast::Sender<RepoEvent>,
pub local_podman_ci_available: Arc<AtomicBool>,
ci_job_tx: Arc<OnceCell<CiJobSender>>,
/// Registry of connected remote CI runners
pub runner_registry: Arc<RunnerRegistry>,
}
impl GlobalState {
/// Build a full URI from a path using the configured base URL
pub fn uri(&self, path: &str) -> String {
format!("{}{}", self.config.base_url, path)
}
/// Check if any CI backend is available and enabled
pub fn ci_available(&self) -> bool {
// CI is available if we have local podman OR connected remote runners OR configured runner tokens
self.local_podman_ci_available.load(Ordering::Relaxed)
|| self.runner_registry.count() > 0
|| !self.config.runner_tokens.is_empty()
}
/// Create a new GlobalState instance
pub fn new(config: AppConfig, process_start: Instant) -> Result<Self> {
fs::create_dir_all(&config.dir_root)?;
fs::create_dir_all(&config.git_root)?;
fs::create_dir_all(&config.session_root)?;
fs::create_dir_all(&config.ci_root)?;
let auth = AuthStore::new(config.dir_root.clone());
// Register runner tokens from config
for token_config in &config.runner_tokens {
let runner_token = RunnerToken::new(&token_config.token, token_config.label.clone());
if let Err(e) = runner_token.save(&auth) {
eprintln!("Failed to register runner token '{}': {}", token_config.label, e);
}
}
let auth = Arc::new(auth);
// Create broadcast channel for SSE events
// 256 buffer - lagging receivers will drop old events
let (event_tx, _) = broadcast::channel(256);
// Create repository service
let repo = Arc::new(RepoService::new(
config.git_root.clone(),
event_tx.clone(),
Arc::clone(&auth),
));
// Create runner registry
let runner_registry = Arc::new(RunnerRegistry::new());
Ok(Self {
auth,
repo,
process_start,
config: Arc::new(config),
event_tx,
local_podman_ci_available: Arc::new(AtomicBool::new(false)),
ci_job_tx: Arc::new(OnceCell::new()),
runner_registry,
})
}
/// Emit a repository event to all SSE listeners
pub fn emit_event(&self, event: RepoEvent) {
// Ignore send errors (no receivers is fine)
let _ = self.event_tx.send(event);
}
/// Set the CI job sender (can only be called once)
pub fn set_ci_sender(&self, sender: Box<dyn Any + Send + Sync>) {
let _ = self.ci_job_tx.set(sender);
}
/// Get the CI job sender (returns None if not set)
pub fn ci_sender(&self) -> Option<&(dyn Any + Send + Sync)> {
self.ci_job_tx.get().map(|b| b.as_ref())
}
}