use std::sync::Arc;
use concurrent_queue::ConcurrentQueue;
use foundation_core::valtron;
use foundation_nativeapis::signal::{signal_task, SignalKind};
use foundation_nativeapis::valtron::FileWatcherTask;
use crate::builder::{CargoBuilder, ProjectBuilderTask};
use crate::runner::BinaryRunnerTask;
use crate::types::ProjectDefinition;
use crate::ToolingError;
pub struct DevService {
pub project: ProjectDefinition,
}
impl DevService {
#[must_use]
pub fn new(project: ProjectDefinition) -> Self {
Self { project }
}
pub fn start(&self) -> Result<(), ToolingError> {
let build_complete_queue: Arc<ConcurrentQueue<()>> =
Arc::new(ConcurrentQueue::unbounded());
let running_queue: Arc<ConcurrentQueue<()>> =
Arc::new(ConcurrentQueue::unbounded());
let mut build_watcher = FileWatcherTask::new()
.map_err(|e| ToolingError::Watch(e.to_string()))?;
for dir in &self.project.build_directories {
build_watcher
.watch(std::path::Path::new(dir), true)
.map_err(|e| ToolingError::Watch(e.to_string()))?;
}
let build_events_rx = build_watcher.subscribe();
valtron::execute(build_watcher, None)
.map_err(|e| ToolingError::Watch(e.to_string()))?;
let mut reload_watcher = FileWatcherTask::new()
.map_err(|e| ToolingError::Watch(e.to_string()))?;
for dir in &self.project.reload_directories {
reload_watcher
.watch(std::path::Path::new(dir), true)
.map_err(|e| ToolingError::Watch(e.to_string()))?;
}
valtron::execute(reload_watcher, None)
.map_err(|e| ToolingError::Watch(e.to_string()))?;
let builder = ProjectBuilderTask::new(build_events_rx, build_complete_queue.clone())
.builder(CargoBuilder {
workspace_root: self.project.workspace_root.clone(),
crate_name: self.project.crate_name.clone(),
build_args: self.project.build_arguments.clone(),
skip_check: self.project.skip_rust_checks,
})
.with_stop_on_failure(self.project.stop_on_failure);
valtron::execute(builder, None)
.map_err(|e| ToolingError::Build(e.to_string()))?;
let runner = BinaryRunnerTask::new(
self.project.clone(),
build_complete_queue,
running_queue,
);
valtron::execute(runner, None)
.map_err(|e| ToolingError::Run(e.to_string()))?;
let (sig_task, bus) = signal_task()
.map_err(|e| ToolingError::Watch(e.to_string()))?;
valtron::send(sig_task)
.map_err(|e| ToolingError::Watch(e.to_string()))?;
let sig_sub = bus.subscribe();
tracing::info!("Dev service started — press Ctrl+C to stop");
loop {
match sig_sub.pop() {
Ok(event) => match event.kind {
SignalKind::Interrupt | SignalKind::Terminate => {
tracing::info!("Received {}, shutting down", event.kind);
break;
}
SignalKind::Hangup => {
tracing::info!("Received SIGHUP — reload not yet implemented");
}
SignalKind::Quit => {
tracing::info!("Received SIGQUIT — shutting down");
break;
}
},
Err(_) => break, }
}
Ok(())
}
}
pub fn run_dev_server(project: ProjectDefinition) -> Result<(), ToolingError> {
let dev_service = DevService::new(project);
let _guard = valtron::initialize_pool(42, None);
dev_service.start()
}