use std::{os::raw::c_int, pin::Pin, ptr};
use crate::{
ffi,
properties::Properties,
subscribe_options::SubscribeOptions,
token::{Token, TokenInner},
types::*,
};
#[derive(Debug)]
pub struct ResponseOptions {
pub(crate) copts: ffi::MQTTAsync_responseOptions,
data: Pin<Box<ResponseOptionsData>>,
}
#[derive(Debug, Default, Clone)]
struct ResponseOptionsData {
props: Properties,
sub_opts: Option<Vec<ffi::MQTTSubscribe_options>>,
}
impl ResponseOptions {
pub(crate) fn new<T>(mqtt_version: u32, tok: T) -> Self
where
T: Into<Token>,
{
let mut copts = Self::c_options(mqtt_version);
copts.context = tok.into().into_raw();
Self::from_data(copts, ResponseOptionsData::default())
}
fn c_options(mqtt_version: u32) -> ffi::MQTTAsync_responseOptions {
if mqtt_version < 5 {
ffi::MQTTAsync_responseOptions {
onSuccess: Some(TokenInner::on_success),
onFailure: Some(TokenInner::on_failure),
..ffi::MQTTAsync_responseOptions::default()
}
}
else {
ffi::MQTTAsync_responseOptions {
onSuccess5: Some(TokenInner::on_success5),
onFailure5: Some(TokenInner::on_failure5),
..ffi::MQTTAsync_responseOptions::default()
}
}
}
fn from_data(mut copts: ffi::MQTTAsync_responseOptions, data: ResponseOptionsData) -> Self {
let mut data = Box::pin(data);
copts.properties = data.props.cprops;
let (p, n) = match data.sub_opts {
Some(ref mut sub_opts) => (sub_opts.as_mut_ptr(), sub_opts.len() as c_int),
_ => (ptr::null_mut(), 0),
};
copts.subscribeOptionsList = p;
copts.subscribeOptionsCount = n;
Self { copts, data }
}
pub fn properties(&self) -> &Properties {
&self.data.props
}
}
impl Default for ResponseOptions {
fn default() -> Self {
Self::from_data(
ffi::MQTTAsync_responseOptions::default(),
ResponseOptionsData::default(),
)
}
}
#[derive(Default)]
pub struct ResponseOptionsBuilder {
copts: ffi::MQTTAsync_responseOptions,
data: ResponseOptionsData,
}
impl ResponseOptionsBuilder {
pub fn new() -> Self {
Self {
copts: ResponseOptions::c_options(MQTT_VERSION_5),
data: ResponseOptionsData::default(),
}
}
pub fn token<T>(&mut self, tok: T) -> &mut Self
where
T: Into<Token>,
{
self.copts.context = tok.into().into_raw();
self
}
pub fn properties(&mut self, props: Properties) -> &mut Self {
self.data.props = props;
self
}
pub fn subscribe_options(&mut self, opts: SubscribeOptions) -> &mut Self {
self.copts.subscribeOptions = opts.copts;
self
}
pub fn subscribe_many_options(&mut self, opts: &[SubscribeOptions]) -> &mut Self {
match opts {
[] => {}
[opts] => self.copts.subscribeOptions = opts.copts,
_ => self.data.sub_opts = Some(opts.iter().map(|opt| opt.copts).collect()),
}
self
}
pub fn finalize(&self) -> ResponseOptions {
ResponseOptions::from_data(self.copts, self.data.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::token::Token;
#[test]
fn test_new_v3() {
let tok = Token::new();
let opts = ResponseOptions::new(MQTT_VERSION_3_1_1, tok.clone());
let inner = Token::into_raw(tok);
assert!(opts.copts.onSuccess.is_some());
assert!(opts.copts.onFailure.is_some());
assert_eq!(inner, opts.copts.context);
assert!(opts.copts.onSuccess5.is_none());
assert!(opts.copts.onFailure5.is_none());
let _ = unsafe { Token::from_raw(inner) };
}
#[test]
fn test_new_v5() {
let tok = Token::new();
let opts = ResponseOptions::new(MQTT_VERSION_5, tok.clone());
let inner = Token::into_raw(tok);
assert!(opts.copts.onSuccess.is_none());
assert!(opts.copts.onFailure.is_none());
assert_eq!(inner, opts.copts.context);
assert!(opts.copts.onSuccess5.is_some());
assert!(opts.copts.onFailure5.is_some());
let _ = unsafe { Token::from_raw(inner) };
}
#[test]
fn test_sub_opts() {
let tok = Token::new();
let sub_opts = SubscribeOptions::with_no_local();
let opts = ResponseOptionsBuilder::new()
.token(tok.clone())
.subscribe_options(sub_opts)
.finalize();
let inner = Token::into_raw(tok);
assert!(opts.copts.onSuccess.is_none());
assert!(opts.copts.onFailure.is_none());
assert_eq!(inner, opts.copts.context);
assert!(opts.copts.onSuccess5.is_some());
assert!(opts.copts.onFailure5.is_some());
assert!(opts.copts.subscribeOptions.noLocal != 0);
let _ = unsafe { Token::from_raw(inner) };
}
#[test]
fn test_sub_many_opts() {
let tok = Token::new();
let sub_opts = vec![SubscribeOptions::with_no_local(); 4];
let opts = ResponseOptionsBuilder::new()
.token(tok.clone())
.subscribe_many_options(&sub_opts)
.finalize();
let inner = Token::into_raw(tok);
assert!(opts.copts.onSuccess.is_none());
assert!(opts.copts.onFailure.is_none());
assert_eq!(inner, opts.copts.context);
assert!(opts.copts.onSuccess5.is_some());
assert!(opts.copts.onFailure5.is_some());
assert_eq!(0, opts.copts.subscribeOptions.noLocal);
assert_eq!(4, opts.copts.subscribeOptionsCount);
assert!(!opts.copts.subscribeOptionsList.is_null());
unsafe {
let sub_opts_list = std::slice::from_raw_parts(opts.copts.subscribeOptionsList, 4);
assert!(sub_opts_list[0].noLocal != 0);
assert!(sub_opts_list[1].noLocal != 0);
assert!(sub_opts_list[2].noLocal != 0);
assert!(sub_opts_list[3].noLocal != 0);
}
let _ = unsafe { Token::from_raw(inner) };
}
}