#![deny(missing_docs)]
use std::time::Duration;
use crate::{
ffi,
properties::Properties,
reason_code::ReasonCode,
token::{Token, TokenInner},
};
#[derive(Debug)]
pub struct DisconnectOptions {
pub(crate) copts: ffi::MQTTAsync_disconnectOptions,
props: Properties,
}
impl DisconnectOptions {
pub fn new() -> Self {
Self::default()
}
fn from_data(mut copts: ffi::MQTTAsync_disconnectOptions, props: Properties) -> Self {
copts.properties = props.cprops;
Self { copts, props }
}
pub fn set_token(&mut self, tok: Token) {
self.copts.onSuccess = Some(TokenInner::on_success);
self.copts.onFailure = Some(TokenInner::on_failure);
self.copts.context = tok.into_raw();
}
pub fn reason_code(&self) -> ReasonCode {
ReasonCode::from(self.copts.reasonCode)
}
pub fn properties(&self) -> &Properties {
&self.props
}
}
impl Default for DisconnectOptions {
fn default() -> Self {
Self::from_data(
ffi::MQTTAsync_disconnectOptions::default(),
Properties::default(),
)
}
}
impl Clone for DisconnectOptions {
fn clone(&self) -> Self {
Self::from_data(self.copts, self.props.clone())
}
}
unsafe impl Send for DisconnectOptions {}
unsafe impl Sync for DisconnectOptions {}
#[derive(Debug, Default)]
pub struct DisconnectOptionsBuilder {
copts: ffi::MQTTAsync_disconnectOptions,
props: Properties,
}
impl DisconnectOptionsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
let secs = timeout.as_secs();
self.copts.timeout = if secs == 0 { 1 } else { secs as i32 };
self
}
pub fn reason_code(&mut self, reason_code: ReasonCode) -> &mut Self {
self.copts.reasonCode = reason_code as ffi::MQTTReasonCodes;
self
}
pub fn publish_will_message(&mut self) -> &mut Self {
self.reason_code(ReasonCode::DisconnectWithWillMessage)
}
pub fn properties(&mut self, props: Properties) -> &mut Self {
self.props = props;
self
}
pub fn finalize(&self) -> DisconnectOptions {
DisconnectOptions::from_data(self.copts, self.props.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{properties::PropertyCode, reason_code::NormalDisconnection};
use std::{os::raw::c_char, thread};
const STRUCT_ID: [c_char; 4] = [
b'M' as c_char,
b'Q' as c_char,
b'T' as c_char,
b'D' as c_char,
];
const STRUCT_VERSION: i32 = ffi::DISCONNECT_OPTIONS_STRUCT_VERSION;
#[test]
fn test_new() {
let opts = DisconnectOptions::new();
assert_eq!(STRUCT_ID, opts.copts.struct_id);
assert_eq!(STRUCT_VERSION, opts.copts.struct_version);
}
#[test]
fn test_send() {
let opts = DisconnectOptions::new();
let thr = thread::spawn(move || {
assert_eq!(STRUCT_ID, opts.copts.struct_id);
assert_eq!(STRUCT_VERSION, opts.copts.struct_version);
});
let _ = thr.join().unwrap();
}
#[test]
fn test_reason_code() {
let opts = DisconnectOptionsBuilder::new().finalize();
assert_eq!(opts.reason_code(), NormalDisconnection);
let opts = DisconnectOptionsBuilder::new()
.reason_code(ReasonCode::DisconnectWithWillMessage)
.finalize();
assert_eq!(opts.reason_code(), ReasonCode::DisconnectWithWillMessage);
let opts = DisconnectOptionsBuilder::new()
.publish_will_message()
.finalize();
assert_eq!(opts.reason_code(), ReasonCode::DisconnectWithWillMessage);
}
#[test]
fn test_properties() {
let opts = DisconnectOptions::new();
assert!(opts.properties().is_empty());
assert_eq!(opts.properties().len(), 0);
let opts = DisconnectOptionsBuilder::new().finalize();
assert!(opts.properties().is_empty());
assert_eq!(opts.properties().len(), 0);
let mut props = Properties::new();
props
.push_int(PropertyCode::SessionExpiryInterval, 1000)
.unwrap();
props
.push_val(PropertyCode::ReasonString, "causeIwanna")
.unwrap();
let opts = DisconnectOptionsBuilder::new().properties(props).finalize();
let props = opts.properties();
assert!(!props.is_empty());
assert_eq!(props.len(), 2);
assert_eq!(
props.get_int(PropertyCode::SessionExpiryInterval),
Some(1000)
);
assert_eq!(
props.get_val::<String>(PropertyCode::ReasonString),
Some("causeIwanna".to_string())
);
assert_eq!(props.get_int(PropertyCode::ContentType), None);
let props = Properties::from_c_struct(&opts.copts.properties);
assert_eq!(props.len(), 2);
assert_eq!(
props.get_int(PropertyCode::SessionExpiryInterval),
Some(1000)
);
assert_eq!(
props.get_val::<String>(PropertyCode::ReasonString),
Some("causeIwanna".to_string())
);
}
}