use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct PluginHttpSection {
pub mount_prefix: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_seconds: Option<u64>,
}
impl PluginHttpSection {
pub fn validate(&self) -> Result<(), String> {
let p = &self.mount_prefix;
if p.is_empty() {
return Err("mount_prefix cannot be empty".into());
}
if !p.starts_with('/') {
return Err(format!("mount_prefix must start with `/`; got `{p}`"));
}
if p.contains('?') || p.contains('#') {
return Err(format!("mount_prefix cannot contain `?` or `#`; got `{p}`"));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validate_accepts_simple_prefix() {
let s = PluginHttpSection {
mount_prefix: "/whatsapp".into(),
timeout_seconds: None,
};
assert!(s.validate().is_ok());
}
#[test]
fn validate_rejects_empty() {
let s = PluginHttpSection::default();
assert!(s.validate().is_err());
}
#[test]
fn validate_rejects_unanchored() {
let s = PluginHttpSection {
mount_prefix: "whatsapp".into(),
timeout_seconds: None,
};
assert!(s.validate().is_err());
}
#[test]
fn validate_rejects_query_separator() {
let s = PluginHttpSection {
mount_prefix: "/whatsapp?instance=default".into(),
timeout_seconds: None,
};
assert!(s.validate().is_err());
}
#[test]
fn deserializes_minimal_toml() {
let toml = r#"
mount_prefix = "/whatsapp"
"#;
let s: PluginHttpSection = toml::from_str(toml).expect("parse");
assert_eq!(s.mount_prefix, "/whatsapp");
assert!(s.timeout_seconds.is_none());
}
#[test]
fn deserializes_with_timeout() {
let toml = r#"
mount_prefix = "/oauth"
timeout_seconds = 60
"#;
let s: PluginHttpSection = toml::from_str(toml).expect("parse");
assert_eq!(s.timeout_seconds, Some(60));
}
}