use std::ffi::OsString;
use std::path::PathBuf;
use std::sync::{Arc, PoisonError, RwLock};
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_log::warn;
use bevy_reflect::prelude::*;
use rig_ecs::agent::SpawnedBy;
use rig_ecs::session_log::ReflectSaved;
use rig_harness_protocol::{Home, env, secret_variables};
use rig_tools::process::Launcher;
#[derive(Resource, Clone, Debug)]
pub struct RigHome(pub Home);
impl RigHome {
pub fn of(world: &World) -> Home {
world
.get_resource::<Self>()
.map_or_else(Home::from_env, |home| home.0.clone())
}
}
impl Default for RigHome {
fn default() -> Self {
Self(Home::from_env())
}
}
#[derive(Resource, Clone, Debug, PartialEq, Eq)]
pub struct ProjectRoot(pub PathBuf);
impl Default for ProjectRoot {
fn default() -> Self {
Self(std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")))
}
}
#[derive(Component, Reflect, Clone, Debug, PartialEq, Eq)]
#[reflect(Component, Saved, Clone, Debug, PartialEq)]
pub struct WorkDir(pub PathBuf);
#[derive(Resource, Clone, Debug)]
pub struct Processes {
launcher: Arc<RwLock<Launcher>>,
secrets: Arc<[OsString]>,
}
impl Processes {
pub fn launcher(&self) -> Launcher {
let launcher = self
.launcher
.read()
.unwrap_or_else(PoisonError::into_inner)
.clone();
let kept: Vec<OsString> = self
.secrets
.iter()
.filter(|secret| !launcher.unset_env.contains(secret))
.cloned()
.collect();
launcher.without_env(kept)
}
pub fn set(&self, launcher: Launcher) {
*self
.launcher
.write()
.unwrap_or_else(PoisonError::into_inner) = launcher;
}
}
impl Default for Processes {
fn default() -> Self {
let launcher = Launcher::default().without_env(env::AGENT_ONLY);
Self {
launcher: Arc::new(RwLock::new(launcher)),
secrets: secret_variables().into(),
}
}
}
#[derive(Resource, Clone, Debug)]
pub struct TokioRuntime(pub tokio::runtime::Handle);
pub struct ServicesPlugin;
impl Plugin for ServicesPlugin {
fn build(&self, app: &mut App) {
if !app.world().contains_resource::<TokioRuntime>() {
match rig_http::runtime::handle() {
Ok(handle) => {
app.insert_resource(TokioRuntime(handle));
}
Err(failure) => warn!("no tokio runtime for plugins: {failure}"),
}
}
app.init_resource::<RigHome>()
.init_resource::<ProjectRoot>()
.init_resource::<Processes>()
.add_observer(inherit_work_dir);
}
}
fn inherit_work_dir(
spawned: On<Add<SpawnedBy>>,
parents: Query<&SpawnedBy>,
dirs: Query<&WorkDir>,
mut commands: Commands,
) {
let parent = parents.get(spawned.entity).map(|&SpawnedBy(parent)| parent);
if let Some(dir) = parent.ok().and_then(|parent| dirs.get(parent).ok()) {
commands
.entity(spawned.entity)
.try_insert_if_new(dir.clone());
}
}
#[cfg(test)]
mod tests;