use crate::xdg::Env;
use std::path::{Path, PathBuf};
pub const DIR: &str = "wire";
pub const ANCHORS: &str = "ca.pem";
pub const ADDRESS: &str = "address";
pub const REMEDY: &str = "make wire-certs";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Role {
Server,
Client,
Window,
}
pub const LEAVES: [Role; 3] = [Role::Server, Role::Client, Role::Window];
impl Role {
pub fn leaf(self) -> String {
match self {
Role::Server => "server".to_owned(),
Role::Client => "client".to_owned(),
Role::Window => "window".to_owned(),
}
}
pub fn common_name(self) -> String {
match self {
Role::Server => "yog-server".to_owned(),
Role::Client => "yog-client".to_owned(),
Role::Window => crate::registry::WINDOW.to_owned(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Material {
pub anchors: PathBuf,
pub chain: PathBuf,
pub key: PathBuf,
pub address: String,
}
pub fn dir(world: &Env) -> PathBuf {
world.yog_data_root().join(DIR)
}
pub fn read(world: &Env, role: Role) -> Result<Option<Material>, String> {
read_dir(&dir(world), role)
}
pub fn read_dir(dir: &Path, role: Role) -> Result<Option<Material>, String> {
let leaf = role.leaf();
let wanted = [
ANCHORS.to_owned(),
format!("{leaf}.pem"),
format!("{leaf}.key"),
ADDRESS.to_owned(),
];
let missing: Vec<&String> = wanted.iter().filter(|f| !dir.join(f).is_file()).collect();
if missing.len() == wanted.len() {
return Ok(None);
}
if !missing.is_empty() {
let names: Vec<&str> = missing.iter().map(|f| f.as_str()).collect();
return Err(format!(
"the wire is half-provisioned at {}: missing {} — run `{REMEDY}`",
dir.display(),
names.join(", ")
));
}
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 — run `{REMEDY}`",
dir.join(ADDRESS).display()
));
}
Ok(Some(Material {
anchors: dir.join(ANCHORS),
chain: dir.join(format!("{leaf}.pem")),
key: dir.join(format!("{leaf}.key")),
address,
}))
}
#[cfg(test)]
mod tests;