use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct PluginPublicTunnelSection {
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub enabled: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub close_on_event: Option<String>,
}
impl PluginPublicTunnelSection {
pub fn is_unset(&self) -> bool {
!self.enabled && self.close_on_event.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_section_is_unset() {
let s = PluginPublicTunnelSection::default();
assert!(s.is_unset());
}
#[test]
fn enabled_alone_round_trips() {
let toml_src = r#"enabled = true"#;
let parsed: PluginPublicTunnelSection = toml::from_str(toml_src).unwrap();
assert!(parsed.enabled);
assert!(parsed.close_on_event.is_none());
}
#[test]
fn enabled_with_close_event_round_trips() {
let toml_src = r#"
enabled = true
close_on_event = "plugin.lifecycle.whatsapp.tunnel_done"
"#;
let parsed: PluginPublicTunnelSection = toml::from_str(toml_src).unwrap();
assert!(parsed.enabled);
assert_eq!(
parsed.close_on_event.as_deref(),
Some("plugin.lifecycle.whatsapp.tunnel_done"),
);
}
#[test]
fn deny_unknown_fields_rejects_typo() {
let toml_src = r#"
enabled = true
clsoe_on_event = "x"
"#;
let err = toml::from_str::<PluginPublicTunnelSection>(toml_src).unwrap_err();
assert!(err.to_string().contains("unknown field"));
}
#[test]
fn skip_serializing_if_unset_emits_empty_toml() {
let s = PluginPublicTunnelSection::default();
let out = toml::to_string(&s).unwrap();
assert!(out.trim().is_empty(), "expected empty TOML, got: {out:?}");
}
}