use std::path::Path;
use anyhow::{Context, bail};
use toml_edit::DocumentMut;
use trusty_mpm::core::architect_session::{poll_session_name, validate_session_name};
use super::config::{Edit, parse_user_config};
use super::{ARCHITECT_SESSION, user_config_path};
pub(crate) const SESSION_KEY: &str = "session";
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SessionNames {
architect: String,
poll: String,
}
impl SessionNames {
pub(crate) fn new(name: &str) -> anyhow::Result<Self> {
validate_session_name(name).map_err(|why| anyhow::anyhow!("invalid --session: {why}"))?;
Ok(Self {
architect: name.to_owned(),
poll: poll_session_name(name),
})
}
pub(crate) fn default_names() -> Self {
Self {
architect: ARCHITECT_SESSION.to_owned(),
poll: poll_session_name(ARCHITECT_SESSION),
}
}
pub(crate) fn architect(&self) -> &str {
&self.architect
}
pub(crate) fn poll(&self) -> &str {
&self.poll
}
}
pub(crate) fn recorded(doc: &DocumentMut, path: &Path) -> anyhow::Result<Option<String>> {
let Some(table) = doc.get("supervisor").and_then(|t| t.as_table_like()) else {
return Ok(None);
};
let Some(item) = table.get(SESSION_KEY) else {
return Ok(None);
};
let name = item.as_str().with_context(|| {
format!(
"`supervisor.{SESSION_KEY}` in {} is not a string",
path.display()
)
})?;
validate_session_name(name).map_err(|why| {
anyhow::anyhow!("`supervisor.{SESSION_KEY}` in {}: {why}", path.display())
})?;
Ok(Some(name.to_owned()))
}
pub(crate) fn recorded_names(home: &Path) -> Result<SessionNames, String> {
let path = user_config_path(home);
let raw = match std::fs::read_to_string(&path) {
Ok(raw) => raw,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Ok(SessionNames::default_names());
}
Err(e) => return Err(format!("cannot read {}: {e}", path.display())),
};
let names = parse_user_config(&raw, &path)
.and_then(|(doc, _)| recorded(&doc, &path))
.and_then(|name| {
name.map_or_else(
|| Ok(SessionNames::default_names()),
|n| SessionNames::new(&n),
)
});
names.map_err(|e| format!("cannot read the recorded session name: {e:#}"))
}
pub(crate) fn record_name(raw: &str, name: &SessionNames, path: &Path) -> anyhow::Result<Edit> {
let (mut doc, _) = parse_user_config(raw, path)?;
let current = recorded(&doc, path)?;
let effective = current.as_deref().unwrap_or(ARCHITECT_SESSION);
if effective == name.architect() {
return Ok(Edit::Unchanged);
}
let table = doc
.entry("supervisor")
.or_insert_with(toml_edit::table)
.as_table_like_mut()
.with_context(|| format!("`supervisor` in {} is not a table", path.display()))?;
table.insert(SESSION_KEY, toml_edit::value(name.architect()));
let text = doc.to_string();
let (check, _) = parse_user_config(&text, path)?;
if recorded(&check, path)?.as_deref() != Some(name.architect()) {
bail!(
"internal error: the edited {} does not record session {}",
path.display(),
name.architect()
);
}
Ok(Edit::Changed(text))
}
#[cfg(test)]
#[path = "session_tests.rs"]
mod tests;