use std::path::PathBuf;
use anyhow::{Result, bail};
use clap::Args;
use crate::settings::{self, Environment, Missing};
const LIMINAL_ADDRESSES_ENV: &str = "AION_WORKER_LIMINAL_ADDRESSES";
const TASK_QUEUE_ENV: &str = "AION_TASK_QUEUE";
const IDENTITY_ENV: &str = "AION_WORKER_IDENTITY";
#[derive(Clone, Debug, Args)]
pub(crate) struct AgentArgs {
pub(crate) awl: Option<PathBuf>,
#[arg(long = "liminal-address")]
pub(crate) liminal_addresses: Vec<String>,
#[arg(long)]
pub(crate) task_queue: Option<String>,
#[arg(long)]
pub(crate) identity: Option<String>,
#[arg(long)]
pub(crate) node: Option<String>,
#[arg(long)]
pub(crate) check: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ConnectionSettings {
pub(crate) addresses: Vec<String>,
pub(crate) identity: String,
pub(crate) node: Option<String>,
}
impl AgentArgs {
pub(crate) fn requested_queue(&self, environment: &Environment) -> Option<String> {
settings::optional(self.task_queue.clone(), TASK_QUEUE_ENV, environment)
}
pub(crate) fn document(&self) -> Result<&std::path::Path> {
match &self.awl {
Some(document) => Ok(document.as_path()),
None => bail!(
"`aion worker agent` serves the queue an `.awl` document declares, and no \
document was named.\n\n aion worker agent <FILE.awl> --liminal-address \
<HOST:PORT> --identity <NAME>\n\nEverything about the LAUNCH — the harness \
kind, the concurrency, the reconnect settings, the agent's command, working \
directory, permission policy and shutdown grace, and the environment \
variables the agent subprocess is given — is declared in that document's \
`harness` section and is no longer a flag. Check a document with `aion awl \
check <FILE.awl>`; the checker names any setting it is missing."
),
}
}
}
pub(crate) fn resolve(args: &AgentArgs, environment: &Environment) -> Result<ConnectionSettings> {
let mut missing = Vec::new();
let addresses = resolve_addresses(args, environment, &mut missing);
let identity = settings::resolve_one(
args.identity.clone(),
IDENTITY_ENV,
"--identity",
environment,
&mut missing,
);
settings::refuse_if_missing(missing)?;
let Some(identity) = identity else {
bail!("--identity must be resolved before use");
};
Ok(ConnectionSettings {
addresses,
identity,
node: args.node.clone(),
})
}
fn resolve_addresses(
args: &AgentArgs,
environment: &Environment,
missing: &mut Vec<Missing>,
) -> Vec<String> {
let from_flags = args
.liminal_addresses
.iter()
.map(|address| address.trim().to_owned())
.filter(|address| !address.is_empty())
.collect::<Vec<_>>();
if !from_flags.is_empty() {
return from_flags;
}
let from_environment = environment
.read(LIMINAL_ADDRESSES_ENV)
.map(|value| {
value
.split(',')
.map(str::trim)
.filter(|address| !address.is_empty())
.map(str::to_owned)
.collect::<Vec<_>>()
})
.unwrap_or_default();
if from_environment.is_empty() {
missing.push(Missing {
flag: "--liminal-address",
variable: LIMINAL_ADDRESSES_ENV,
});
}
from_environment
}