use std::path::{Path, PathBuf};
pub const ANCHORS: &str = "ca.pem";
pub const CHAIN: &str = "client.pem";
pub const KEY: &str = "client.key";
pub const ADDRESS: &str = "address";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Whose {
Own,
Elsewhere,
}
impl Whose {
pub fn remedy(self) -> String {
let four = format!("`{ANCHORS}`, `{CHAIN}`, `{KEY}` and `{ADDRESS}`");
match self {
Self::Own => format!(
"if an engine runs on this box its material already exists — its own boot \
minted it — so the act is a COPY: take {four} out of that engine's wire \
directory ($XDG_DATA_HOME/yog/wire) and put them here, under those names. \
`{ADDRESS}` must name a stated host:port; an engine that bound `:0` asked the \
kernel for a port and told only its own window, so re-issue with one stated \
(`WIRE_HOST=<host> WIRE_PORT=<port> yog wire-certs`) before copying. If the \
engine is on ANOTHER box, mint a leaf there instead (`WIRE_LEAF=<name> yog \
wire-certs` — the assignment before the verb) and carry the same four files \
here; the seat mints nothing"
),
Self::Elsewhere => format!(
"the pair is minted on the host that issued it — `WIRE_LEAF=<name> yog \
wire-certs` there, the assignment BEFORE the verb, because `WIRE_LEAF` is an \
environment variable and not an argument it parses — and carried here by hand \
as {four}, under those names; the seat mints nothing"
),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Material {
pub anchors: PathBuf,
pub chain: PathBuf,
pub key: PathBuf,
pub address: String,
pub pairing: Option<super::rendezvous::Pairing>,
}
pub fn read_dir(dir: &Path, whose: Whose) -> Result<Option<Material>, String> {
let wanted = [ANCHORS, CHAIN, KEY, ADDRESS];
let missing: Vec<&str> = wanted
.iter()
.copied()
.filter(|f| !dir.join(f).is_file())
.collect();
if missing.len() == wanted.len() {
return Ok(None);
}
if !missing.is_empty() {
return Err(format!(
"{} is half-provisioned: missing {} — {}",
dir.display(),
missing.join(", "),
whose.remedy()
));
}
let address = std::fs::read_to_string(dir.join(ADDRESS))
.unwrap_or_default()
.trim()
.to_owned();
if address.is_empty() {
return Err(format!(
"{} names no address; it must hold one host:port — {}",
dir.join(ADDRESS).display(),
whose.remedy()
));
}
Ok(Some(Material {
anchors: dir.join(ANCHORS),
chain: dir.join(CHAIN),
key: dir.join(KEY),
address,
pairing: super::rendezvous::read_dir(dir)?,
}))
}
#[cfg(test)]
mod tests;