#[macro_export]
macro_rules! define_upnp_operation {
(
operation: $op_struct:ident,
action: $action:literal,
service: $service:ident,
request: {
$($field:ident: $field_type:ty),* $(,)?
},
response: $response_type:ty,
payload: |$req_param:ident| $payload_expr:expr,
parse: |$xml_param:ident| $parse_expr:expr $(,)?
) => {
paste! {
#[derive(serde::Serialize, Clone, Debug, PartialEq)]
pub struct [<$op_struct Request>] {
$(pub $field: $field_type,)*
pub instance_id: u32,
}
#[derive(serde::Deserialize, Debug, Clone, PartialEq)]
pub struct [<$op_struct Response>];
pub struct $op_struct;
impl $crate::operation::UPnPOperation for $op_struct {
type Request = [<$op_struct Request>];
type Response = $response_type;
const SERVICE: $crate::service::Service = $crate::service::Service::$service;
const ACTION: &'static str = $action;
fn build_payload(request: &Self::Request) -> Result<String, $crate::operation::ValidationError> {
request.validate($crate::operation::ValidationLevel::Basic)?;
let $req_param = request;
Ok($payload_expr)
}
fn parse_response(xml: &xmltree::Element) -> Result<Self::Response, $crate::error::ApiError> {
let $xml_param = xml;
$parse_expr
}
}
pub fn [<$op_struct:snake>]($($field: $field_type),*) -> $crate::operation::OperationBuilder<$op_struct> {
let request = [<$op_struct Request>] {
$($field,)*
instance_id: 0,
};
$crate::operation::OperationBuilder::new(request)
}
}
};
}
#[macro_export]
macro_rules! define_operation_with_response {
(
operation: $op_struct:ident,
action: $action:literal,
service: $service:ident,
request: {
$($field:ident: $field_type:ty),* $(,)?
},
response: $response_struct:ident {
$($resp_field:ident: $resp_type:ty),* $(,)?
},
xml_mapping: {
$($xml_field:ident: $xml_path:literal),* $(,)?
} $(,)?
) => {
paste! {
#[derive(serde::Serialize, Clone, Debug, PartialEq)]
pub struct [<$op_struct Request>] {
$(pub $field: $field_type,)*
pub instance_id: u32,
}
#[derive(serde::Deserialize, Debug, Clone, PartialEq)]
pub struct $response_struct {
$(pub $resp_field: $resp_type,)*
}
pub struct $op_struct;
impl $crate::operation::UPnPOperation for $op_struct {
type Request = [<$op_struct Request>];
type Response = $response_struct;
const SERVICE: $crate::service::Service = $crate::service::Service::$service;
const ACTION: &'static str = $action;
fn build_payload(request: &Self::Request) -> Result<String, $crate::operation::ValidationError> {
request.validate($crate::operation::ValidationLevel::Basic)?;
#[allow(unused_mut)]
let mut xml = format!("<InstanceID>{}</InstanceID>", request.instance_id);
$(
let field_name = stringify!($field);
let capitalized = if field_name.is_empty() {
field_name.to_string()
} else {
let mut chars = field_name.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().chain(chars).collect(),
}
};
let escaped = $crate::operation::xml_escape(&format!("{}", request.$field));
xml.push_str(&format!("<{}>{}</{}>",
capitalized,
escaped,
capitalized));
)*
Ok(xml)
}
fn parse_response(xml: &xmltree::Element) -> Result<Self::Response, $crate::error::ApiError> {
$(let $xml_field = xml
.get_child($xml_path)
.and_then(|e| e.get_text())
.and_then(|s| s.parse().ok())
.unwrap_or_default();)*
Ok($response_struct {
$($resp_field: $xml_field,)*
})
}
}
pub fn [<$op_struct:snake>]($($field: $field_type),*) -> $crate::operation::OperationBuilder<$op_struct> {
let request = [<$op_struct Request>] {
$($field,)*
instance_id: 0,
};
$crate::operation::OperationBuilder::new(request)
}
}
};
}
#[cfg(test)]
mod tests {
#[test]
fn test_macro_compilation() {
}
}