use std::sync::{Mutex, OnceLock};
use serde::{Deserialize, Serialize};
use crate::paths::machine_config_path;
use crate::utils::atomic::atomic_write;
use crate::utils::lock::LockRecover;
#[allow(
clippy::missing_docs_in_private_items,
reason = "split-out module keeps the file under the linecheck limit"
)]
mod glob_match;
pub(crate) use glob_match::*;
#[derive(Debug, Default, Deserialize, Serialize)]
struct MachineToml {
name: Option<String>,
max_concurrent_runs: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MachineSource {
Env,
File,
Generated,
Hostname,
}
impl MachineSource {
pub const fn label(self) -> &'static str {
match self {
Self::Env => "MOADIM_MACHINE env",
Self::File => "machine.local.toml",
Self::Generated => "auto-generated (first run)",
Self::Hostname => "system hostname",
}
}
}
pub fn current_machine() -> String {
resolve().0
}
pub fn resolve() -> (String, MachineSource) {
let env = std::env::var("MOADIM_MACHINE").ok();
let file = read_machine_file();
if let Some(name) = non_empty(env) {
return (name, MachineSource::Env);
}
if let Some(name) = non_empty(file) {
return (name, MachineSource::File);
}
let generated = generate_name();
match set_machine(&generated) {
Ok(()) => {
log::warn!(
"no machine name configured; generated {generated:?} — run `moadim machine set <name>` to choose your own"
);
(generated, MachineSource::Generated)
}
Err(err) => {
log::warn!("failed to save generated machine name: {err}; falling back to hostname");
(hostname(), MachineSource::Hostname)
}
}
}
fn generate_name() -> String {
format!(
"machine-{}",
&uuid::Uuid::new_v4().simple().to_string()[..8]
)
}
#[cfg(test)]
fn resolve_from(
env: Option<String>,
file: Option<String>,
hostname: String,
) -> (String, MachineSource) {
if let Some(name) = non_empty(env) {
return (name, MachineSource::Env);
}
if let Some(name) = non_empty(file) {
return (name, MachineSource::File);
}
(hostname, MachineSource::Hostname)
}
fn non_empty(value: Option<String>) -> Option<String> {
value
.map(|raw| raw.trim().to_string())
.filter(|trimmed| !trimmed.is_empty())
}
fn hostname() -> String {
gethostname::gethostname().to_string_lossy().into_owned()
}
fn read_machine_toml() -> MachineToml {
std::fs::read_to_string(machine_config_path())
.ok()
.and_then(|text| toml::from_str(&text).ok())
.unwrap_or_default()
}
fn read_machine_file() -> Option<String> {
read_machine_toml().name
}
fn machine_toml_lock() -> &'static Mutex<()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
}
include!("write_machine_toml.rs");