rupnp/scpd/
mod.rs

1use crate::{
2    find_in_xml,
3    utils::{self, HttpResponseExt, HyperBodyExt},
4    Error,
5};
6use bytes::Bytes;
7use http::Uri;
8use http_body_util::Empty;
9use hyper_util::rt::TokioExecutor;
10use roxmltree::{Document, Node};
11use ssdp_client::URN;
12use std::rc::Rc;
13
14mod action;
15mod state_variable;
16pub use action::*;
17pub use state_variable::*;
18
19/// Service Control Protocol Description.
20/// It contains information about a particular service, more specifically its actions and state
21/// variables.
22#[derive(Debug)]
23pub struct SCPD {
24    urn: URN,
25    state_variables: Vec<Rc<StateVariable>>,
26    actions: Vec<Action>,
27}
28impl SCPD {
29    pub fn urn(&self) -> &URN {
30        &self.urn
31    }
32    pub fn state_variables(&self) -> &[Rc<StateVariable>] {
33        &self.state_variables
34    }
35    pub fn actions(&self) -> &[Action] {
36        &self.actions
37    }
38
39    /// Fetches the SCPD description.
40    /// The `urn` has to be provided because it isn't included in the description.
41    pub(crate) async fn from_url(url: &Uri, urn: URN) -> Result<Self, Error> {
42        let body = hyper_util::client::legacy::Client::builder(TokioExecutor::new())
43            .build_http::<Empty<Bytes>>()
44            .get(url.clone())
45            .await?
46            .err_if_not_200()?
47            .into_body()
48            .bytes()
49            .await?;
50        let body = std::str::from_utf8(&body)?;
51
52        let document = Document::parse(body)?;
53        let scpd = utils::find_root(&document, "scpd", "Service Control Point Definition")?;
54
55        #[allow(non_snake_case)]
56        let (state_variables, actions) = find_in_xml! { scpd => serviceStateTable, actionList };
57
58        let state_variables: Vec<_> = state_variables
59            .children()
60            .filter(Node::is_element)
61            .map(StateVariable::from_xml)
62            .map(|sv| sv.map(Rc::new))
63            .collect::<Result<_, _>>()?;
64        let actions = actions
65            .children()
66            .filter(Node::is_element)
67            .map(|node| Action::from_xml(node, &state_variables))
68            .collect::<Result<_, _>>()?;
69
70        Ok(Self {
71            urn,
72            state_variables,
73            actions,
74        })
75    }
76}