use crate::config::PermissionsMode;
use crate::permissions::{resolve_v07_default_mode, startup_banner_line};
#[derive(Debug, Clone)]
pub struct BannerInputs {
pub configured_permissions_mode: Option<PermissionsMode>,
pub auto_generated_keypair_path: Option<String>,
pub identity_disabled: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BannerLine {
Info(String),
Warn(String),
}
impl BannerLine {
#[must_use]
pub fn message(&self) -> &str {
match self {
BannerLine::Info(s) | BannerLine::Warn(s) => s,
}
}
#[must_use]
pub fn is_warn(&self) -> bool {
matches!(self, BannerLine::Warn(_))
}
}
#[must_use]
pub fn compose_banner(inputs: &BannerInputs) -> Vec<BannerLine> {
let mut out: Vec<BannerLine> = Vec::new();
let (mode, migration_warning) = resolve_v07_default_mode(inputs.configured_permissions_mode);
out.push(BannerLine::Info(startup_banner_line(mode)));
if let Some(w) = migration_warning {
out.push(BannerLine::Warn(w));
}
if let Some(path) = &inputs.auto_generated_keypair_path {
out.push(BannerLine::Warn(format!(
"auto-generated identity keypair at {path} — consider backing up"
)));
} else if inputs.identity_disabled {
out.push(BannerLine::Info(
"identity: disabled in config — link signing skipped".to_string(),
));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn banner_unconfigured_mode_emits_enforce_and_warning() {
let lines = compose_banner(&BannerInputs {
configured_permissions_mode: None,
auto_generated_keypair_path: None,
identity_disabled: false,
});
assert_eq!(lines[0], BannerLine::Info("permissions: enforce".into()));
assert!(lines[1].is_warn(), "expected warn line, got {:?}", lines[1]);
assert!(
lines[1]
.message()
.contains("v0.7.0 default changed to enforce")
);
assert_eq!(lines.len(), 2);
}
#[test]
fn banner_configured_advisory_skips_migration_warning() {
let lines = compose_banner(&BannerInputs {
configured_permissions_mode: Some(PermissionsMode::Advisory),
auto_generated_keypair_path: None,
identity_disabled: false,
});
assert_eq!(lines.len(), 1);
assert_eq!(lines[0], BannerLine::Info("permissions: advisory".into()));
}
#[test]
fn banner_configured_enforce_skips_migration_warning() {
let lines = compose_banner(&BannerInputs {
configured_permissions_mode: Some(PermissionsMode::Enforce),
auto_generated_keypair_path: None,
identity_disabled: false,
});
assert_eq!(lines.len(), 1);
assert_eq!(lines[0], BannerLine::Info("permissions: enforce".into()));
}
#[test]
fn banner_includes_auto_gen_keypair_line() {
let lines = compose_banner(&BannerInputs {
configured_permissions_mode: Some(PermissionsMode::Enforce),
auto_generated_keypair_path: Some("/tmp/k.priv".into()),
identity_disabled: false,
});
assert_eq!(lines.len(), 2);
assert!(lines[1].is_warn());
let msg = lines[1].message();
assert!(
msg.contains("auto-generated identity keypair at /tmp/k.priv"),
"got: {msg}"
);
assert!(msg.contains("consider backing up"));
}
#[test]
fn banner_identity_disabled_emits_info_line_when_no_autogen() {
let lines = compose_banner(&BannerInputs {
configured_permissions_mode: Some(PermissionsMode::Enforce),
auto_generated_keypair_path: None,
identity_disabled: true,
});
assert_eq!(lines.len(), 2);
assert_eq!(
lines[1],
BannerLine::Info("identity: disabled in config — link signing skipped".to_string())
);
}
#[test]
fn banner_identity_disabled_yields_to_autogen_line() {
let lines = compose_banner(&BannerInputs {
configured_permissions_mode: Some(PermissionsMode::Enforce),
auto_generated_keypair_path: Some("/tmp/k.priv".into()),
identity_disabled: true,
});
assert_eq!(lines.len(), 2);
assert!(
lines[1]
.message()
.contains("auto-generated identity keypair")
);
}
}