use crate::Result;
use crate::daemon::Daemon;
use crate::daemon_id::DaemonId;
use crate::daemon_status::DaemonStatus;
use crate::ipc::client::IpcClient;
use crate::pitchfork_toml::PitchforkToml;
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct DaemonListEntry {
pub id: DaemonId,
pub daemon: Daemon,
pub is_disabled: bool,
pub is_available: bool, }
pub async fn get_all_daemons(client: &IpcClient) -> Result<Vec<DaemonListEntry>> {
let config = PitchforkToml::all_merged()?;
let state_file = crate::state_file::StateFile::read(&*crate::env::PITCHFORK_STATE_FILE)?;
let state_daemons: Vec<Daemon> = state_file.daemons.values().cloned().collect();
let disabled_daemons = client.get_disabled_daemons().await?;
let disabled_set: HashSet<DaemonId> = disabled_daemons.into_iter().collect();
build_daemon_list(state_daemons, disabled_set, config)
}
pub async fn get_all_daemons_direct(
supervisor: &crate::supervisor::Supervisor,
) -> Result<Vec<DaemonListEntry>> {
let config = PitchforkToml::all_merged()?;
let state_file = supervisor.state_file.lock().await;
let state_daemons: Vec<Daemon> = state_file.daemons.values().cloned().collect();
let disabled_set: HashSet<DaemonId> = state_file.disabled.clone().into_iter().collect();
drop(state_file);
build_daemon_list(state_daemons, disabled_set, config)
}
pub async fn get_daemon_direct(
supervisor: &crate::supervisor::Supervisor,
id: &DaemonId,
) -> Result<Option<DaemonListEntry>> {
let pitchfork_id = DaemonId::pitchfork();
if *id == pitchfork_id {
return Ok(None);
}
let state_file = supervisor.state_file.lock().await;
if let Some(daemon) = state_file.daemons.get(id).cloned() {
let is_disabled = state_file.disabled.contains(id);
drop(state_file);
return Ok(Some(DaemonListEntry {
id: id.clone(),
daemon,
is_disabled,
is_available: false,
}));
}
let is_disabled = state_file.disabled.contains(id);
drop(state_file);
let config = PitchforkToml::all_merged()?;
if let Some(daemon_config) = config.daemons.get(id) {
return Ok(Some(DaemonListEntry {
id: id.clone(),
daemon: build_placeholder_daemon(id, daemon_config),
is_disabled,
is_available: true,
}));
}
let namespaces = PitchforkToml::read_global_namespaces();
for (_, entry) in namespaces {
match PitchforkToml::all_merged_from(&entry.dir) {
Ok(ns_config) => {
if let Some(daemon_config) = ns_config.daemons.get(id) {
return Ok(Some(DaemonListEntry {
id: id.clone(),
daemon: build_placeholder_daemon(id, daemon_config),
is_disabled,
is_available: true,
}));
}
}
Err(e) => {
log::warn!("Failed to load namespace from {}: {e}", entry.dir.display());
}
}
}
Ok(None)
}
fn build_placeholder_daemon(
id: &DaemonId,
daemon_config: &crate::pitchfork_toml::PitchforkTomlDaemon,
) -> Daemon {
Daemon {
id: id.clone(),
status: DaemonStatus::Stopped,
port: daemon_config.port.clone(),
depends: vec![],
env: None,
watch: vec![],
watch_mode: daemon_config.watch_mode,
watch_base_dir: None,
mise: daemon_config.mise,
user: daemon_config.user.clone(),
active_port: None,
slug: None,
proxy: None,
memory_limit: daemon_config.memory_limit,
cpu_limit: daemon_config.cpu_limit,
..Daemon::default()
}
}
fn build_daemon_list(
state_daemons: Vec<Daemon>,
disabled_set: HashSet<DaemonId>,
config: PitchforkToml,
) -> Result<Vec<DaemonListEntry>> {
let mut entries = Vec::new();
let mut seen_ids = HashSet::new();
let pitchfork_id = DaemonId::pitchfork();
for daemon in state_daemons {
if daemon.id == pitchfork_id {
continue; }
seen_ids.insert(daemon.id.clone());
entries.push(DaemonListEntry {
id: daemon.id.clone(),
is_disabled: disabled_set.contains(&daemon.id),
is_available: false,
daemon,
});
}
for (daemon_id, daemon_config) in &config.daemons {
if *daemon_id == pitchfork_id || seen_ids.contains(daemon_id) {
continue;
}
let placeholder = build_placeholder_daemon(daemon_id, daemon_config);
entries.push(DaemonListEntry {
id: daemon_id.clone(),
daemon: placeholder,
is_disabled: disabled_set.contains(daemon_id),
is_available: true,
});
seen_ids.insert(daemon_id.clone());
}
let namespaces = PitchforkToml::read_global_namespaces();
for (ns_name, entry) in namespaces {
match PitchforkToml::all_merged_from(&entry.dir) {
Ok(ns_config) => {
for (daemon_id, daemon_config) in &ns_config.daemons {
if *daemon_id == pitchfork_id || seen_ids.contains(daemon_id) {
continue;
}
let placeholder = build_placeholder_daemon(daemon_id, daemon_config);
entries.push(DaemonListEntry {
id: daemon_id.clone(),
daemon: placeholder,
is_disabled: disabled_set.contains(daemon_id),
is_available: true,
});
seen_ids.insert(daemon_id.clone());
}
}
Err(e) => {
log::warn!(
"Failed to load namespace '{ns_name}' from {}: {e}",
entry.dir.display()
);
}
}
}
Ok(entries)
}