use std::collections::HashMap;
use crate::error::OnvifError;
use crate::soap::{SoapError, XmlNode};
#[derive(Debug, Clone)]
pub struct PullPointSubscription {
pub reference_url: String,
pub termination_time: String,
}
impl PullPointSubscription {
pub(crate) fn from_xml(resp: &XmlNode) -> Result<Self, OnvifError> {
let reference_url = resp
.path(&["SubscriptionReference", "Address"])
.map(|n| n.text().to_string())
.ok_or_else(|| SoapError::missing("SubscriptionReference/Address"))?;
let termination_time = resp
.child("TerminationTime")
.map(|n| n.text().to_string())
.unwrap_or_default();
Ok(Self {
reference_url,
termination_time,
})
}
}
#[derive(Debug, Clone)]
pub struct NotificationMessage {
pub topic: String,
pub utc_time: String,
pub source: HashMap<String, String>,
pub data: HashMap<String, String>,
}
impl NotificationMessage {
pub(crate) fn vec_from_xml(resp: &XmlNode) -> Vec<Self> {
resp.children_named("NotificationMessage")
.map(Self::from_node)
.collect()
}
fn from_node(node: &XmlNode) -> Self {
let topic = node
.child("Topic")
.map(|n| n.text().trim().to_string())
.unwrap_or_default();
let msg = node
.path(&["Message", "Message"])
.or_else(|| node.child("Message"));
let utc_time = msg
.and_then(|n| n.attr("UtcTime").map(str::to_string))
.unwrap_or_default();
let source = msg
.and_then(|n| n.child("Source"))
.map(parse_simple_items)
.unwrap_or_default();
let data = msg
.and_then(|n| n.child("Data"))
.map(parse_simple_items)
.unwrap_or_default();
Self {
topic,
utc_time,
source,
data,
}
}
}
fn parse_simple_items(node: &XmlNode) -> HashMap<String, String> {
node.children_named("SimpleItem")
.filter_map(|item| {
let name = item.attr("Name")?.to_string();
let value = item.attr("Value").unwrap_or("").to_string();
Some((name, value))
})
.collect()
}
#[derive(Debug, Clone, Default)]
pub struct EventProperties {
pub topics: Vec<String>,
}
impl EventProperties {
pub(crate) fn from_xml(resp: &XmlNode) -> Result<Self, OnvifError> {
let topic_set = resp
.child("TopicSet")
.ok_or_else(|| SoapError::missing("TopicSet"))?;
let mut topics = Vec::new();
flatten_topics(topic_set, String::new(), &mut topics);
Ok(Self { topics })
}
}
fn flatten_topics(node: &XmlNode, prefix: String, out: &mut Vec<String>) {
for child in &node.children {
if child.local_name == "MessageDescription" {
continue;
}
let path = if prefix.is_empty() {
child.local_name.clone()
} else {
format!("{prefix}/{}", child.local_name)
};
let has_non_desc = child
.children
.iter()
.any(|n| n.local_name != "MessageDescription");
if has_non_desc {
flatten_topics(child, path, out);
} else {
out.push(path);
}
}
}